let result = ["1": "1", "2": nil, "3": "3"].reduce(into: [String: String]()) { (result, x) in
if let value = x.value { result[x.key] = value }
}
// ["1": "1", "3": "3"]
However now that we have Dictionary.mapValues() from Swift 4 and the Sequence.flatMap() will be renamed to Sequence.compactMap() with [SE-0187] . I want compactMapValues to remove nil value in dictionary as below.
I can see how this would be useful; it seems like a reasonable addition to complete the API. This is a different operation than mapValues(_:) followed by filter(_:).
Do you have a specific implementation in mind? (I suspect it might be possible to use Dictionary internals to eliminate rehashing in some cases. But it's probably better to just build a brand new Dictionary from scratch, like you do with reduce.)
I can think of 2 implementations. 1) using reduce(into:_:) as posted above; and 2) Dictionary(uniqueKeysWithValues: srcDict.lazy.filter { $0.1 != nil }). They are not-really-quite-straightforward, perhaps, but neither benefits from being in the standard library from the performance standpoint. So I'm in the fence about including it.
Upd: and just to contradict myself, the .lazy.filter variant is incorrect as it does not unwrap the optionals. So, one extra point to being included.
I think making new Dictionary as I posted is better in viewing from performance, but I only don't have any good idea to use former, so feel free to post better idea if somebody has.
That works! Dictionary.filter(_:) doesn't try to be clever with hash table internals, either.
I think we should have more practical examples to motivate inclusion. Dictionaries of optional values do not occur very often in my experience -- I'd typically just declare a non-optional value and filter out nils during insertion. For other cases, a filter followed by mapValues would often work just as well as compactMapValues.
We try to avoid mutation and write code that's declarative, so our uses tend to be where we want to pass a dictionary literal to a call site that expects non-optional values, but where some of our inputs may be optional.
These use cases may be minor enough to not require an implementation in the standard library, but we'll probably continue to add our own extension and benefit from it regardless. It's a bit messy to filter and mapValues everywhere (especially with the force unwrap). What we want is succinctness and safety.
This isn’t quite so much about dictionaries with optional values as transformations of a dictionary’s values that yield an optional. For example:
let d = ["a": "1", "b": "2", "c": "three"]
// This is .compactMapValues(Int.init):
let e = d.mapValues(Int.init).filter({ $0.value != nil }).mapValues({ $0! })
There’s no real savings here from a hashing perspective—the filtering means we’ll need to rehash all the elements—but it would save on allocating more space than we need for a bunch of nil values.
Yes, I think this is worth an implementation and a small proposal. We've seen clear examples of its usefulness.
We have to consider the cognitive weight of adding too many APIs; however, we already have filter and mapValues, and they feel incomplete without compactMapValues -- leaving it out would be jarring.
I support this idea. I just wanted to write flatMapValues in the current Swift version that was renamed to compactMap(Values) on a dictionary to map the values by doing a dynamic cast as? on the values.