Please take a look at my 2D-Array-Initialization. The code works.
class World(val size_x: Int = 256, val size_y: Int = 256) {
var worldTiles = Array(size_x, { Array(size_y, { WorldTile() }) })
fun generate() {
for( x in 0..size_x-1 ) {
for( y in 0..size_y-1 ) {
worldTiles[x][y] = WorldTile()
}
}
}
}
The problem is that it runs the initialization twice. Basically I want to instantiate the WorldTile-Object in the generate() function. So Line 3 shouldn't call "new WorldTile" there. How can I do that?
Also is that the proper Kotlin way of traversing a 2d-Array?
You can make worldTiles a lateinit property, and do all the initialization in the generate function:
class World(val size_x: Int = 256, val size_y: Int = 256) {
lateinit var worldTiles: Array<Array<WorldTile>>
fun generate() {
worldTiles = Array(size_x, {
Array(size_y, {
WorldTile()
})
})
}
}
If you try to access worldTiles before calling generate you will get an exception warning that it hasn't been initialized yet.
To initialise all to a fixed value:
// A 6x5 array of Int, all set to 0.
var m = Array(6) {Array(5) {0} }
To initialise with a lambda:
// a 6x5 Int array initialise i + j
var m = Array(6) { i -> Array(5) { j -> i + j } }
Another way: Here is an example of initialising a 2D array of Float numbers (3 by 6):
var a = Array(3) { FloatArray(6)} // define an 3x6 array of float numbers
for(i:Int in 0 until a.size) {
for(j : Int in 0 until a[i].size) {
a[i][j] = 0f // initialize with your value here.
}
}
val twoDimStringArray= arrayOf(
arrayOf("first","second"),
arrayOf("foo"),
arrayOf("bar","great kotlin")
)
for (i in twoDimStringArray){
for(j in i){
println(j)
}
}
first
second
foo
bar
great kotlin
A bit late but could help to somebody if is working with strings
//init 2d array with a fixed size:
var data2 = Array<Array<String>>(2) { arrayOf()}
// fill the 2d array
data2[0] = arrayOf("123","Freddy x","27")
data2[1] = arrayOf("124","Elon y","45")
cheers!
Related
I am trying to read and/or console print on of the elements of my array which consist of 28 structs. I can not access any of the struct on my array is says that "Cannot call value of non-function type '[Ficha]'" and I can't find why.. Sorry kind of a newbie on swift.. The section commented is where I discovered the problem but I can't even print one of the elements.
Please help
import UIKit
import Foundation
struct Ficha {
var numero: Int
var ladoA = 0
var ladoB = 0
}
extension Ficha: CustomStringConvertible {
var description: String {
return "f\(numero) \(ladoA)/\(ladoB)"
}
}
var dSet = [Ficha] ()
var rSet = [Int: Ficha] ()
func setDset () {
dSet = []
rSet = [:]
var fj = 0
var x1: Double = 0
var ficha1 : Ficha
var fichanum = 0
for x in 0...6 {
for y in x...6 {
fichanum = fichanum + 1
dSet.append(Ficha.init(numero: fichanum, ladoA: x, ladoB: y))
}
}
dSet.shuffle()
}
setDset()
print (dSet(2))
Using dSet with parenthesis is incorrect, that's the syntax for a function. So the line:
print(dSet(2))
is assuming there's a function that returns something:
func dSet(_ x: Int) -> Something {
return Something
}
To access an item at an index you use the square brackets, so it should be:
print(dSet[2])
Which will print the item at index 2 in the array dSet.
As others have pointed out, you access members of a collection using subscripts, which are invoked via [], not () (which is for regular function calls).
You can simplify this code quite a bit, btw:
import UIKit
struct Ficha {
var numero: Int
var ladoA = 0
var ladoB = 0
}
extension Ficha: CustomStringConvertible {
var description: String {
return "f\(numero) \(ladoA)/\(ladoB)"
}
}
func calculateDset() -> [Ficha] {
let xyPairs = (0...6).flatMap { x in
(x...6).map { y in (x: x, y: y) }
}
return zip(1..., xyPairs)
.map { (fichanum, pair) in
return Ficha(numero: fichanum, ladoA: pair.x, ladoB: pair.y)
}
.shuffled()
}
let dSet = calculateDset()
print(dSet[2])
UIKit already imports Foundation
Make functions return values, don't set them directly.
Don't set initial values to variables, only to immediate overwrite them with something else.
rSet, fj, x1, and ficha1 are unused, delete them.
Having a two demential string array and the row has only two items,
i.e. {{"1", "a"} {"2", "b"}, {"3", "c"} ... ...}
the invert function to do the invert
public static String[][] invert(final String[][] array) {
final String[][] newarray = new String[array.length][2];
for(int i = 0; i<array.length; i++) {
newarray[i][0] = array[i][1];
newarray[i][1] = array[i][0];
}
return newarray;
}
translate to kotlin:
fun invert(array: Array<Array<String?>>): Array<Array<String?>> {
val newarray = Array<Array<String?>>(array.size) { arrayOfNulls(2) }
for (i in array.indices) {
newarray[i][0] = array[i][1]
newarray[i][1] = array[i][0]
}
return newarray
}
Because of the arrayOfNulls(2), has to define the two demential array to be Array<Array<String?>>
but the return type of Array<Array<String?>> breaks the a lot of the code which expecting Array<Array<String>>
Using the val newarray = Array<Array<String>>(array.size) { Array<String>(2) {""}} to force the array initialized all rows with empty string (so not null).
fun invert(array: Array<Array<String>>): Array<Array<String>> {
val newarray = Array<Array<String>>(array.size) { Array<String>(2) {""}}
for (i in array.indices) {
newarray[i][0] = array[i][1]
newarray[i][1] = array[i][0]
}
return newarray
}
Is it the only way, or is there some function like
fun strinagArrayOf(vararg elements: String): Array<String>
That translation threw you off the track; there's no reason to initialize and loop again through the fields.
This is signature of constructor you're using:
/**
* Creates a new array with the specified [size], where each element is calculated by calling the specified
* [init] function. The [init] function returns an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> T)
You actually get to pass you own initialization logic for each item in the init function. You even get index of item you're initializing (it's the only argument so it's not explicitly declared but it's accesible as it).
So to get what you need, You can simply initialize inverted array right there (changed param from array to input for readability):
fun invert(input: Array<Array<String>>) = Array(input.size) {arrayOf(input[it][1], input[it][0]) }
This method works fine. However, I think it is not functional.
fun getCopy(array: Array<BooleanArray>): Array<BooleanArray> {
val copy = Array(array.size) { BooleanArray(array[0].size) { false } }
for (i in array.indices) {
for (j in array[i].indices) {
copy[i][j] = array[i][j]
}
}
return copy
}
Is there a more functional way?
You can make use of clone like so:
fun Array<BooleanArray>.copy() = map { it.clone() }.toTypedArray()
or if you'd like to save some allocations:
fun Array<BooleanArray>.copy() = arrayOfNulls<ByteArray>(size).let { copy ->
forEachIndexed { i, bytes -> copy[i] = bytes.clone() }
copy
} as Array<BooleanArray>
or even more concise as suggested by #hotkey:
fun Array<BooleanArray>.copy() = Array(size) { get(it).clone() }
What about using copyOf()?
val copyOfArray = array.copyOf()
Returns new array which is a copy of the original array
Reference here
I want to store structs inside an array, access and change the values of the struct in a for loop.
struct testing {
var value:Int
}
var test1 = testing(value: 6 )
test1.value = 2
// this works with no issue
var test2 = testing(value: 12 )
var testings = [ test1, test2 ]
for test in testings{
test.value = 3
// here I get the error:"Can not assign to 'value' in 'test'"
}
If I change the struct to class it works. Can anyone tell me how I can change the value of the struct.
Besides what said by #MikeS, remember that structs are value types. So in the for loop:
for test in testings {
a copy of an array element is assigned to the test variable. Any change you make on it is restricted to the test variable, without doing any actual change to the array elements. It works for classes because they are reference types, hence the reference and not the value is copied to the test variable.
The proper way to do that is by using a for by index:
for index in 0..<testings.count {
testings[index].value = 15
}
in this case you are accessing (and modifying) the actual struct element and not a copy of it.
Well I am going to update my answer for swift 3 compatibility.
When you are programming many you need to change some values of objects that are inside a collection. In this example we have an array of struct and given a condition we need to change the value of a specific object. This is a very common thing in any development day.
Instead of using an index to determine which object has to be modified I prefer to use an if condition, which IMHO is more common.
import Foundation
struct MyStruct: CustomDebugStringConvertible {
var myValue:Int
var debugDescription: String {
return "struct is \(myValue)"
}
}
let struct1 = MyStruct(myValue: 1)
let struct2 = MyStruct(myValue: 2)
let structArray = [struct1, struct2]
let newStructArray = structArray.map({ (myStruct) -> MyStruct in
// You can check anything like:
if myStruct.myValue == 1 {
var modified = myStruct
modified.myValue = 400
return modified
} else {
return myStruct
}
})
debugPrint(newStructArray)
Notice all the lets, this way of development is safer.
The classes are reference types, it's not needed to make a copy in order to change a value, like it happens with structs. Using the same example with classes:
class MyClass: CustomDebugStringConvertible {
var myValue:Int
init(myValue: Int){
self.myValue = myValue
}
var debugDescription: String {
return "class is \(myValue)"
}
}
let class1 = MyClass(myValue: 1)
let class2 = MyClass(myValue: 2)
let classArray = [class1, class2]
let newClassArray = classArray.map({ (myClass) -> MyClass in
// You can check anything like:
if myClass.myValue == 1 {
myClass.myValue = 400
}
return myClass
})
debugPrint(newClassArray)
To simplify working with value types in arrays you could use following extension (Swift 3):
extension Array {
mutating func modifyForEach(_ body: (_ index: Index, _ element: inout Element) -> ()) {
for index in indices {
modifyElement(atIndex: index) { body(index, &$0) }
}
}
mutating func modifyElement(atIndex index: Index, _ modifyElement: (_ element: inout Element) -> ()) {
var element = self[index]
modifyElement(&element)
self[index] = element
}
}
Example usage:
testings.modifyElement(atIndex: 0) { $0.value = 99 }
testings.modifyForEach { $1.value *= 2 }
testings.modifyForEach { $1.value = $0 }
How to change Array of Structs
for every element:
itemsArray.indices.forEach { itemsArray[$0].someValue = newValue }
for specific element:
itemsArray.indices.filter { itemsArray[$0].propertyToCompare == true }
.forEach { itemsArray[$0].someValue = newValue }
You have enough of good answers. I'll just tackle the question from a more generic angle.
As another example to better understand value types and what it means they get copied:
struct Item {
var value:Int
}
func change (item: Item, with value: Int){
item.value = value // cannot assign to property: 'item' is a 'let' constant
}
That is because item is copied, when it comes in, it is immutable — as a convenience.
Had you made Item a class type then you were able to change its value.
var item2 = item1 // mutable COPY created
item2.value = 10
print(item2.value) // 10
print(item1.value) // 5
This is very tricky answer. I think, You should not do like this:
struct testing {
var value:Int
}
var test1 = testing(value: 6)
var test2 = testing(value: 12)
var ary = [UnsafeMutablePointer<testing>].convertFromArrayLiteral(&test1, &test2)
for p in ary {
p.memory.value = 3
}
if test1.value == test2.value {
println("value: \(test1.value)")
}
For Xcode 6.1, array initialization will be
var ary = [UnsafeMutablePointer<testing>](arrayLiteral: &test1, &test2)
It is possible to use the map function to get this effect - essentially creating a new array
itemsArray = itemsArray.map {
var card = $0
card.isDefault = aCard.token == token
return card
}
I ended up recreating a new array of struct see the example below.
func updateDefaultCreditCard(token: String) {
var updatedArray: [CreditCard] = []
for aCard in self.creditcards {
var card = aCard
card.isDefault = aCard.token == token
updatedArray.append(card)
}
self.creditcards = updatedArray
}
I tried Antonio's answer which seemed quite logical but to my surprise it does not work. Exploring this further I tried the following:
struct testing {
var value:Int
}
var test1 = testing(value: 6 )
var test2 = testing(value: 12 )
var testings = [ test1, test2 ]
var test1b = testings[0]
test1b.value = 13
// I would assume this is same as test1, but it is not test1.value is still 6
// even trying
testings[0].value = 23
// still the value of test1 did not change.
// so I think the only way is to change the whole of test1
test1 = test1b
I'm struggling with something that shouldn't be too difficult but I can't figure it out I have a number of Arrays with different values and I want to find the common values all of the Arrays have, see example below:
var arrayOne:Array = ["1","2","3"];
var arrayTwo:Array = ["1","2","7"];
var arrayThree:Array = ["1","2","9","12"];
_resultArray = ["1","2"];
Any help is appreciated.
You can do something like:
///Returns common values between to arrays
function getCommonValues(array1:Array, array2:Array):Array
{
var len1:int = array1.length;
var len2:int = array2.length;
var toReturn:Array = new Array();
for(var i:int = 0; i < len1; i++){
for(var n:int = 0; n < len2; n++){
if(array1[i] == array2[n]){
toReturn.push(array1[i]);
}
}
}
return toReturn;
}
Then do something like:
var arrayOneAndTwo:Array = getCommonValues(arrayOne,arrayTwo);
var _resultArray:Array = getCommonValues(arrayOneAndTwo,arrayThree);
Optionally you can modify the function to include all three arrays in the comparison, which would be more efficient.
Edit
If you want to process an unknown amount of arrays you can add:
///Returns common values between X number of sub arrays
function getCommonValuesFromSubArrays(papaArray:Array):Array
{
if(papaArray.length < 2){ return papaArray; }
var toReturn:Array = papaArray[0];
for(var a:int = 1; a < papaArray.length; a++){
toReturn = getCommonValues(toReturn, papaArray[a]);
}
return toReturn;
}
Then something like:
var arr1:Array = ["one","two","three","four","five"];
var arr2:Array = ["one","two","five","six"];
var arr3:Array = ["one","two","three","four","five"];
var arr4:Array = ["one","two","three","four","five"];
var bigOlArray:Array = [arr1,arr2,arr3,arr4];
var _results:Array = getCommonValuesFromSubArrays(bigOlArray);
I would use a function to concatenate all arrays, sort by numerical value, and collect all items that are available exactly as many times as the number of arrays that were passed in as parameters:
var arrayOne : Array = [ "1", "2", "3" ];
var arrayTwo : Array = [ "1", "2", "7" ];
var arrayThree : Array = [ "1", "2", "9", "12" ];
// you can pass in any number of Arrays
trace ( searchArraysForCommonItems ( arrayOne, arrayTwo, arrayThree ) ); // returns ["1", "2"]
function searchArraysForCommonItems ( ...args : * ) : Array
{
var searchArray : Array = [];
for each ( var arr:Array in args)
searchArray = searchArray.concat ( arr );
var resultArray : Array = [];
var last : String;
var times : int = 0;
for each ( var str : String in searchArray.sort ( Array.NUMERIC ))
if (last == str) times++;
else
{
if (times == args.length) resultArray.push ( last );
last = str;
times = 1;
}
return resultArray;
}
Of course, you can (and should) use Vector.<String> instead of Array wherever possible to improve performance, but always remember that Array.sort() is a native function and very fast...
I would use the Array.filter() Function to achieve this:
var _resultArray:Array = arrayOne.filter(
function(item:String, index:int, arr:Array):Boolean
{
return (arrayTwo.indexOf(item) != -1 && arrayThree.indexOf(item));
}
);
This will loop over arrayOne and return an array with the values that both appear also in arrayTwo and arrayThree.
Edit: And here is a function that will take any number of arrays and return the common values:
function getCommonValues(arrayOne:Array, ... arrays:Array):Array
{
var _resultArray:Array = arrayOne.filter(
function(item:String, index:int, arr:Array):Boolean
{
return arrays.every(
function (a:Array, index2:int, arr2:Array):Boolean
{
return a.indexOf(item) != -1;
}
);
}
);
return _resultArray;
}
Usage:
resultArray = getCommonValues(arrayOne, arrayTwo, arrayThree, arrayFour);
The function has another nested closure inside the first one, so might be a bit hard to understand, but I tested it, it works.