Built-in func for not replacing a value in a Dictionary?

I couldn't see anything obvious that replicates this behaviour in an optimal way:

var someDict: [String: String]
...
if someDict["someID"] == nil {
   // The first value with the key takes precedence.
    someDict["someID"] = "someString"
}

i.e I only want to add an item to the dictionary if an item with that key doesn't already exist.
The above code isn't hard to write when needed, but I want to avoid needing the key to be looked up twice.

merge is close, but requires the value to be known upfront, whereas in my case I don't want to calculate it if it's not actually going to be added.

So basically I want something analogous to updateValue like this:

@discardableresult
public mutating func addValueForKeyOnce(_ key Key, value: @autoclosure() -> Value) -> Bool // True if the item was actually added

Apologies if I missed something obvious

func str1() -> String { "1" }        // not executed

var d = [1: "one"]

d[1] = d[1] ?? str1()
d[2] = d[2] ?? "two"

d                                    // [1: "one", 2: "two"]

Whilst shorter to write than the if [d1] == nil {...} equivalent it doesn't solve the issue that the Dictionary is going to require two lookups of the key in whatever hashing it uses internally.

It's also less optimal as its always going to set the value of d1, regardless of whether extant or not.

1 Like

This topic was discussed at the beginning of the month.

Personally I haven't needed to worry about the performance yet, and just do

_ = someDict["someID", valueAddedIfNil: "someString"]
public extension Dictionary {
  subscript(
    key: Key,
    valueAddedIfNil value: @autoclosure() -> Value
  ) -> Value {
    mutating get {
      self[key]
      ?? { [value = value()] in
        self[key] = value
        return value
      } ()
    }
  }
}
3 Likes

Thanks for the reference to the original topic. Might as well end this version now.

@anon9791410's solution addresses the ergonomics aspect of the problem. The discussion that they linked talks about a possible performance issue in very specific situations, i.e., when hashing the dictionary key is noticeably expensive.