In Scala, the + (k -> v)
operator on immutable.Map
returns a new immutable.Map
with the contents of the original, plus the new key/value pair. Similarly, in C#, ImmutableDictionary.add(k, v)
returns a new, updated ImmutableDictionary
.
In Swift, however, Dictionary
appears only to have the mutating updateValue(v, forKey: k)
function and the mutating [k:v]
operator.
I thought maybe I could play some trick with flatten()
, but no luck:
let updated = [original, [newKey: newValue]].flatten()
gets me
Cannot convert value of type '() -> FlattenCollection<[[String : AnyObject]]>'
to specified type '[String : AnyObject]'
How do I create a new, modified immutable Dictionary
from the contents of an existing one?
Update: Based on this answer's note that Swift dictionaries are value types, and this answer's mutable version, I came up with the following extension operator, but I'm not excited about it -- it seems like there must be a cleaner out-of-the-box alternative.
func + <K, V>(left: [K:V], right: [K:V]) -> [K:V] {
var union = left
for (k, v) in right {
union[k] = v
}
return union
}
But maybe the fact (if I understand correctly) that the immutability of Swift dictionaries is a compiler check on let
rather than a matter of different implementation classes means this is the best that can be done?
Update #2: As noted in Jules's answer, modifying immutable dictionaries that aren't specifically optimized to share state between copies (as Swift dictionaries aren't) presents performance problems. For my current use case (AttributedString
attribute dictionaries, which tend to be fairly small) it may still simplify certain things enough to be worth doing, but until and unless Swift implements a shared-state immutable dictionary it's probably not a good idea in the general case -- which is a good reason not to have it as a built-in feature.
To append an element to a dictionary in Swift programming, assign a value to the Dictionary with a key that is not already present. Note that the new_key has to be unique with respect to the keys already present in the dictionary. value does not have any constraints.
In Swift, you have either value types (struct, enum, tuple, array, dict etc) or reference types (classes). If you need to copy a class object, then, you have to implement the methods copyWithZone in your class and then call copy on the object.
Instances of your SharedDictionary will be passed-by-reference (not copied).
Unfortunately, this is a good question because the answer is "you can't". Not yet, anyway--others agree this should be added, because there's a Swift Evolution proposal for this (and some other missing Dictionary
features). It's currently "awaiting review", so you may see a merged()
method that's basically your +
operator in a future version of Swift!
In the meantime, you can use your solution to append entire dictionaries, or for one value at a time:
extension Dictionary {
func appending(_ key: Key, _ value: Value) -> [Key: Value] {
var result = self
result[key] = value
return result
}
}
There's no built-in way to do this right now. You could write your own using an extension (below).
But keep in mind that this will likely copy the dictionary, because dictionaries are copy-on-write, and you're doing exactly that (making a copy, then mutating it). You can avoid all this by just using a mutable variable in the first place :-)
extension Dictionary {
func updatingValue(_ value: Value, forKey key: Key) -> [Key: Value] {
var result = self
result[key] = value
return result
}
}
let d1 = ["a": 1, "b": 2]
d1 // prints ["b": 2, "a": 1]
let d2 = d1.updatingValue(3, forKey: "c")
d1 // still prints ["b": 2, "a": 1]
d2 // prints ["b": 2, "a": 1, "c": 3]
The most straightforward thing to do is to copy to a variable, modify, then re-assign back to a constant:
var updatable = original
updatable[newKey] = newValue
let updated = updatable
Not pretty, obviously, but it could be wrapped into a function easily enough.
extension Dictionary {
func addingValue(_ value: Value, forKey key: Key) -> Dictionary<Key, Value> {
// Could add a guard here to enforce add not update, if needed
var updatable = self
updatable[key] = value
return updatable
}
}
let original = [1 : "One"]
let updated = original.addingValue("Two", forKey: 2)
I don't believe there's a solution other than roll-your-own.
But maybe the fact (if I understand correctly) that the immutability of Swift dictionaries is a compiler check on
let
Right, mutability is specified on the storage, that is, the variable, not on the value.
Do not try to update an immutable dictionary unless it has been specifically designed for immutability.
Immutable dictionaries usually use a data structure (such as a red/black tree with immutable nodes than can be shared between instances or similar) that can generate a modified copy without needing to make copies of the entire content, but only a subset (i.e. they have O(log(n)) copy-and-modify operations) but most dictionaries that are designed for a mutable system and then used with an immutable interface do not, so have O(n) copy-and-modify operations. When your dictionary starts to get larger than a few hundred nodes, you'll really notice the performance difference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With