Swift reference vs value with arrays and dictionaries - arrays

In a nutshell,
I create an object consisting of an Integer and an Array of Strings.
I put something into both the integer and the array
I put the object into a dictionary
I get a new reference to the object and attempt to append to the array. This is where it fails
//: Playground - noun: a place where people can play
class IdAndArray {
var id: Int = 0
var stringArray:[String] = [String]()
}
var dict:[Int:IdAndArray] = [Int:IdAndArray]()
var testRec = IdAndArray()
testRec.id = 1
testRec.stringArray.append("1 line")
dict[56] = testRec
var ref = (dict[56]?.stringArray)!
print(ref.count) // 1
ref.append("2 line")
var anotherRef = (dict[56]?.stringArray)!
print(anotherRef.count) // 1 instead of 2???
I suspect this may be related to this:
Implementing a multimap in Swift with Arrays and Dictionaries
I also suspect it is something to do with optionals but I am forcing the unwrap with the !
For the record, I'm coming from a java background.

Swift arrays and dictionaries are value types. When you assign one that is in a variable to another variable, you are making a copy of the original.
So when you do this:
var ref = (dict[56]?.stringArray)!
ref is an entirely new copy of the array and not the one that is in the dictionary, so modifying it has no effect on the original copy in the dictionary.
If instead you had done:
dict[56]?.stringArray.append("2 line")
then you would have modified the copy that is in the dictionary.
Note: In reality, Swift doesn't make a copy of the array until you modify one of the copies. This is done behind the scenes to avoid unnecessary copying and to keep things quick. The copy logically happens immediately when you assign it, but you wouldn't notice the difference until you start modifying one of the copies, so Swift delays the copy until it matters.
Now consider this change to your code:
var ref = dict[56]!
print(ref.stringArray.count) // 1
ref.stringArray.append("2 line")
var anotherRef = dict[56]!
print(anotherRef.stringArray.count) // 2 this time!!!
Here, ref points to an instance of a class which is a reference type. In this case, both ref and anotherRef point to the same object, and thus you are modifying the array in the dictionary this time.

Related

How to show elements of array?

I have a small problem. I created a large array.
It looks like this:
var Array = [
["text10", "text11", ["text01", "text02"]],
["text20", "text21", ["text11", "text12"]]
]
If we write this way: Array[0] that shows all the elements.
If we write this way: Array[0][0] that shows "text1".
If we write this way: Array[0][2] that shows
-2 elements
-- 0: "text01"
-- 1: "text02"
.
If we write this way: Array[0][2].count or Array[0][2][0] it will not work
How do I choose each item, I need these elements for the tableView
The problem basically is that your inner array is illegal. Swift arrays must consist of elements of a single type. You have two types of element, String and Array Of String. Swift tries to compensate but the result is that double indexing can’t work, not least because there is no way to know whether a particular element will have a String or an Array in it.
The solution is to rearchitect completely. If your array entries all consist of the same pattern String plus String plus Array of String, then the pattern tells you what to do; that should be a custom struct, not an array at all.
as #matt already answered but I want to add this thing
Why Array[0][2].count or Array[0][2][0] not work
If you Define array
var array = [
["text10", "text11", ["text01", "text02"]],
["text20", "text21", ["text11", "text12"]]
]
And when you type array you can see it's type is [[Any]] However it contains String as well as Array
So When you try to get Array[0][2] Swift does not know that your array at position 2 has another array which can have count
EDIT
What you are asking now is Array of dictionary I suggest you to go with model i.e create struct or class and use it instead of dictionary
Now If you want to create dictionary then
var arrOfDict = ["text10" : ["text01", "text02"] , "text11" : ["text11", "text12"]]
And you can access with key name let arrayatZero = arrOfDict["text10"] as? [String]

Swift property observers: why is didSet not called on an array property if the array is amended by appending a new array?

I have a string array and I also wish to create a localised copy of this array (whilst keeping the original array intact).
There are a number of ways to do this, but I thought I might use a property observer to set the contents of the localised array, when the contents of the non-localised array are modified.
I created a test property observer as such:
// Array to store the card theme names
private var cardThemeNames = [String]() {
didSet {
// debug print statement
print( oldValue )
}
}
Interestingly, if I amend the cardThemeNames array one element at a time, then the didSet method of the observer is called, as expected, i.e.:
cardThemeNames.append( "Random" )
However, if I modify the array by appending another array, then disSet is not called, i.e.:
cardThemeNames.append( contentsOf: someOtherArray )
or:
cardThemeNames += someOtherArray
Can anyone explain why this is the case and confirm if property observers generally only work when modifying one element of a collection?
Thanks.

How do you properly setup Unsafe Pointers to access CFDictionary Keys and Values?

I am trying to access the keys and values in a CFDictionary which is created from a CGImageSource. Here is the definition of the function used to access Keys and Values data:
func CFDictionaryGetKeysAndValues(_ theDict: CFDictionary!,
_ keys: UnsafeMutablePointer<UnsafeRawPointer?>!,
_ values: UnsafeMutablePointer<UnsafeRawPointer?>!)
My problem is with creating the two pointers for the Keys and Values arrays. Also, I am really unsure whether I am declaring the Arrays the correct way. Here is the description of the CFDictionaryGetKeysAndValues keys and values parameters:
A C array of pointer-sized values that, on return, is filled with keys from the theDict. The keys and values C arrays are parallel to each other (that is, the items at the same indices form a key-value pair from the dictionary). This value must be a valid pointer to a C array of the appropriate type and size (that is, a size equal to the count of theDict), or NULL if the keys are not required. If the keys are Core Foundation objects, ownership follows the The Get Rule.
Here is my implementation according to what I understand of the Unsafe pointers API, but it gives me unreliable results:
// Get the CFDictionary and count the number of returned properties
let imageProperties: CFDictionary = CGImageSourceCopyProperties(imageSource, nil)!
let propCount = CFDictionaryGetCount(imageProperties)
// Create a stub UnsafeRawPointer used to initialize Array
let string: String = ""
let stubPointer = UnsafeRawPointer(string)
// Initialize an Array of pointers for the Keys and one for the Values
var aKeys: Array<UnsafeRawPointer> = Array(repeating: stubPointer, count: propCount)
var aValues: Array<UnsafeRawPointer> = Array(repeating: stubPointer, count: propCount)
// Create the pointers and !hopefully! access dictionary Keys and Values
withUnsafePointer(to: &aKeys[0], {(ptr: UnsafePointer<UnsafeRawPointer>?) -> Void in
var ptrKeysM = UnsafeRawPointer(ptr)
let ptrKeys = UnsafeMutablePointer(&ptrKeysM)!
ptrKeys.initialize(to: ptrKeysM, count: propCount)
withUnsafePointer(to: &aValues[0], {(ptr2: UnsafePointer<UnsafeRawPointer>?) -> Void in
var ptrValuesM = UnsafeRawPointer(ptr2)
let ptrValues = UnsafeMutablePointer(&ptrValuesM)!
ptrValues.initialize(to: ptrValuesM, count: propCount)
CFDictionaryGetKeysAndValues(imageProperties, ptrKeys, ptrValues)
})
})
Now this compiles correctly. When the CGImageSource only returns 1 property, everything goes well (ie: the application doesn't crash). However, if the CGImageSource returns more than 1 property, I get the following error after the call to CFDictionaryGetKeysAndValues:
error: memory read failed for 0x1f600
Thread 1: EXC_BAD_ACCESS (code=1, address=0x1f637)
You might have guessed that I am totally new to Swift and I still have not wrapped my head around this API for pointers. From what I understand, the GetKeysAndValues call returns pointers to the Keys and Values which are stored in the aKeys and aValues Arrays. Is this correct? Thank you all for your help.

MATLAB: Object Array Property Assignment with Deal

I have a class constructor that takes multiple numeric arrays of the same size and creates an object array of the same size. In the constructor, I am trying to use deal to assign data from each input array to a single index of the object array's only property:
classdef myClass
properties
data
end
methods
function obj = myClass(varargin)
nArg = length(varargin);
for iArg = 1:nArg
dataArg = num2cell(varargin{iArg});
[obj.data(iArg)] = deal(dataArg{:});
end
end
end
end
Unfortunately, I get the following error from deal: The number of outputs should match the number of inputs.
I believe that this is probably occurring because I haven't preallocated the object array before the for loop in the constructor, e.g.:
szObj = size(varargin{1});
cellSzObj = num2cell(szObj);
obj(cellSzObj{:}) = myClass.empty;
However, for various reasons, I want to be able to make this assignment without preallocating the object array.
Is there a way to do this?

Inserting data into an array sequentially

I am currently trying to figure out how to design some sort of loop to insert data into an array sequentially. I'm using Javascript in the Unity3D engine.
Basically, I want to store a bunch of coordinate locations in an array. Whenever the user clicks the screen, my script will grab the coordinate location. The problem is, I'm unsure about how to insert this into an array.
How would I check the array's index to make sure if array[0] is taken, then use array[1]? Maybe some sort of For loop or counter?
Thanks
To just add onto the end of an array, just use .push().
var myArray = [];
var coord1 = [12,59];
var coord2 = [87,23];
myArray.push(coord1);
myArray.push(coord2);
myArray, now contains two items (each which is an array of two coordinates).
Now, you wouldn't do it this way if you were just statically declaring everything as I've done here (you could just statically declare the whole array), but I just whipped up this sample to show you how push works to add an item onto the end of an array.
See https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push for some reference doc on push.
In case you need to know the array's length when reading the array in the future, you can use the .length attribute.
var lengthOfArray = myArray.length;
Using the .push() method as suggested by jfriend00 is my recommendation too, but to answer your question about how to work out what the next index is you can use the array's length property. Because JavaScript arrays are zero-based The length property will return an integer one higher than the current highest index, so length will also be the index value to use if you want to add another item at the end:
anArray[anArray.length] = someValue; // add to end of array
To get the last element in the array you of course say anArray[anArray.length-1].
Note that for most purposes length will give the number of elements in the array, but I said "one higher than the current highest index" above because JavaScript arrays are quite happy for you to skip indexes:
var myArray = [];
myArray[0] = "something";
myArray[1] = "something else";
myArray[50] = "something else again";
alert(myArray.length); // gives '51'
// accessing any unused indexes will return undefined

Resources