Reputation: 447
this is probably a very basic/stupid question: how do I get a reference to a collection in Swift, such that a change to that reference affects the original and vice versa? So if for instance I have the following code:
var a1 = [Int]()
var a2 = a1
a1.append(1)
print(a2)
Can I get a "reference" (or whatever name it would have in Swift) to a1
such that when I change a1
, a2
reflects the same change, and it ends up displaying "[1]" instead of "[]"?
I guess this has to do with collections being primary types, and thus not behaving like other objects, but then I'm at a loss as to how I can play around with collections without them being duplicated all the time.
More specifically, when working with a Dictionary<String, Dictionary<String, Int>>
, what's the best way to update the contents of the nested Dictionary
while minimizing the number of lookups? The following approach used to work in Java but I guess it's different with Swift:
var dict = Dictionary<String, Dictionary<String, Int>>()
var d = dict["a"]
if d == nil {
d = Dictionary()
dict["a"] = d
}
d!["b"] = 1
print("\(dict["a"]!["b"])")
prints "nil"
(Note: if possible, I'd like to avoid multiple dict["a"]!["b"]
lookups)
Thanks!
Upvotes: 4
Views: 1469
Reputation: 59536
Array
, Dictionary
and Set
(among many others) in Swift are structs
. A struct in Swift is a value type so when you assign a value to another variable you create a copy (at least at high level until a real change is done).
This is particularly visibile when you pass a struct to a function
func changeIt(numbers:[Int]) {
var numbers = numbers
numbers.removeFirst()
print("Inside the function \(numbers)") // "Inside function 2, 3"
}
var numbers = [1, 2, 3]
changeIt(numbers)
print("Outside the function \(numbers)") // "Outside the function 1, 2, 3"
You can define a function to receive a reference to a value type adding the keyword inout
before the param name
func changeIt(inout numbers:[Int]) {
numbers.removeFirst()
print("Inside the function \(numbers)") // Inside the function [2, 3]
}
next when you invoke the function you add & before the param to clarify you are passing a reference to it
var numbers = [1, 2, 3]
changeIt(&numbers)
print("Outside the function \(numbers)") // Outside the function [2, 3]
This theory is not directly related to your code snipped. You can simply build the dictionary starting from the deepest elements like this
var dict = [String : [String : Int]]()
let b: Int = (dict["a"]?["b"]) ?? 1
let a: [String : Int] = dict["a"] ?? ["a": b]
dict["a"] = a
As you can see I am building b
and then a
using the value in the dictionary (if present) or a default value.
Finally it looks like you are trying to build a JSON, right? In this case I really suggest you to use SwiftyJSON, it will make things much much easier.
Upvotes: 3
Reputation: 447
I ended up implementing a wrapper around a dictionary:
class HeapDict<K: Hashable, V> : SequenceType {
var dict = [K : V]()
subscript(index: K) -> V? {
get {
return dict[index]
}
set {
dict[index] = newValue
}
}
func generate() -> DictionaryGenerator<K, V> {
return dict.generate()
}
}
This allows for very simple modification of the nested dictionary:
var dict2 = [String : HeapDict<String, Int>]()
var dd = dict2["a"]
if dd == nil {
dd = HeapDict()
dict2["a"] = dd
}
dd!["b"] = 1
From my point of view this addresses my original issue with, I believe, very little overhead. Whether it's a good thing to manipulate collections this way might be another discussion though, and my reason for doing it might simply be my background with other programming languages. Thanks for the answers! :-)
Upvotes: 0
Reputation: 138231
You could do something like this:
func with<T>(inout _ object: T, @noescape action: (inout T) -> ()) {
action(&object)
}
That would let you write:
with(&dict["a"]) { subDict in
subDict!["b"] = 4
subDict!["c"] = 5
}
Upvotes: 3