David Moles David Moles - 8 months ago 59
Swift Question

How do I "append" to an immutable dictionary in Swift?

In Scala, the

operator on
returns a new
with the contents of the original, plus the new key/value pair. Similarly, in C#,
ImmutableDictionary.add(k, v)
returns a new, updated

In Swift, however,
appears only to have the mutating
updateValue(v, forKey: k)
function and the mutating

I thought maybe I could play some trick with
, 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
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
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 (
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.


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