# Add compactMapValues to Dictionary

**URL:** <https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741>\
**Category:** Pitches\
**Created:** [January 24, 2018, 8:23am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741 "2018-01-24T08:23:07Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![d-date](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/d-date/32/19682_2.png) [@d-date](https://forums.swift.org/u/d-date)\
**Post date:** [January 24, 2018, 8:23am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/1 "2018-01-24T08:23:07Z")

</div>

Hi,

I’d like to propose about `compactMapValues` in Dictionary.

When I imagine removing nil value from dictionary at first as below, but it doesn’t works.

```swift
["1" : "1", "2" : nil, "3" : "3"].flatMap { $0 }

// [(key: "2", value: nil), (key: "1", value: Optional("1")), (key: "3", value: Optional("3"))]

```

To avoid this, I’ve heard using `reduce` like this.

```swift
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]](https://github.com/apple/swift-evolution/blob/master/proposals/0187-introduce-filtermap.md) . I want `compactMapValues` to remove nil value in dictionary as below.

```swift
["1" : "1", "2" : nil, "3" : "3"].compactMapValues({ $0 })

// ["1": "1", "3": "3"]

```

How do you think that?

---

<div class="post-metadata">

**Author:** ![akashivskyy](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/akashivskyy/32/1136_2.png) [@akashivskyy](https://forums.swift.org/u/akashivskyy)\
**Post date:** [January 24, 2018, 5:57pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/2 "2018-01-24T17:57:10Z")

</div>

I think this idea simply _makes sense_, given the existence of `Dictionary.mapValues` and `Sequence.compactMap`.

I have been implementing such extensions myself so I'd be happy to see stdlib provide it by default.

---

<div class="post-metadata">

**Author:** ![moiseev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/moiseev/32/281_2.png) [@moiseev](https://forums.swift.org/u/moiseev)\
**Post date:** [January 24, 2018, 7:29pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/3 "2018-01-24T19:29:35Z")

</div>

/cc @Karoly\_Lorentey

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [January 24, 2018, 8:54pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/4 "2018-01-24T20:54:56Z")

</div>

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`.)

---

<div class="post-metadata">

**Author:** ![moiseev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/moiseev/32/281_2.png) [@moiseev](https://forums.swift.org/u/moiseev)\
**Post date:** [January 24, 2018, 9:32pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/5 "2018-01-24T21:32:07Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![d-date](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/d-date/32/19682_2.png) [@d-date](https://forums.swift.org/u/d-date)\
**Post date:** [January 25, 2018, 4:13am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/6 "2018-01-25T04:13:04Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [January 25, 2018, 12:05pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/7 "2018-01-25T12:05:46Z")

</div>

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`.

---

<div class="post-metadata">

**Author:** ![stephencelis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/stephencelis/32/2675_2.png) [@stephencelis](https://forums.swift.org/u/stephencelis)\
**Post date:** [January 25, 2018, 12:40pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/8 "2018-01-25T12:40:50Z")

</div>

> [@lorentey](#):
>
> 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 added a `compact` method to `Dictionary` when I was at Kickstarter and used it throughout:

- [https://github.com/kickstarter/ios-oss/blob/996ba3d8990c85ac9b47dc15e6362a23af49ed69/KsApi/lib/Route.swift#L119-L123](https://github.com/kickstarter/ios-oss/blob/996ba3d8990c85ac9b47dc15e6362a23af49ed69/KsApi/lib/Route.swift#L119-L123)
- [https://github.com/kickstarter/ios-oss/blob/996ba3d8990c85ac9b47dc15e6362a23af49ed69/KsApi/lib/Route.swift#L308-L312](https://github.com/kickstarter/ios-oss/blob/996ba3d8990c85ac9b47dc15e6362a23af49ed69/KsApi/lib/Route.swift#L308-L312)
- [https://github.com/kickstarter/ios-oss/blob/a7636fa76c5c97bc87882ca17212be8e944c795e/Kickstarter-iOS/ViewModels/RootViewModel.swift#L108-L116](https://github.com/kickstarter/ios-oss/blob/a7636fa76c5c97bc87882ca17212be8e944c795e/Kickstarter-iOS/ViewModels/RootViewModel.swift#L108-L116)
- [https://github.com/kickstarter/ios-oss/blob/1b21ce9100edc2700b30f41432f4c6077febba69/Kickstarter-iOS/ViewModels/AppDelegateViewModel.swift#L942](https://github.com/kickstarter/ios-oss/blob/1b21ce9100edc2700b30f41432f4c6077febba69/Kickstarter-iOS/ViewModels/AppDelegateViewModel.swift#L942)

I even recently added a `filterMapValues` (and `filteredValues`) to our code base at Point-Free:

- [https://github.com/pointfreeco/pointfreeco/blob/f97a9e4830a5bdfe778daca8ed771f7fa9eadbc7/Sources/PointFree/Stripe.swift#L306-L311](https://github.com/pointfreeco/pointfreeco/blob/f97a9e4830a5bdfe778daca8ed771f7fa9eadbc7/Sources/PointFree/Stripe.swift#L306-L311)

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.

---

<div class="post-metadata">

**Author:** ![nnnnnnnn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nnnnnnnn/32/264_2.png) [@nnnnnnnn](https://forums.swift.org/u/nnnnnnnn)\
**Post date:** [January 25, 2018, 2:39pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/9 "2018-01-25T14:39:12Z")

</div>

This isn’t quite so much about dictionaries with optional values as transformations of a dictionary’s values that yield an optional. For example:

```swift
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.

---

<div class="post-metadata">

**Author:** ![d-date](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/d-date/32/19682_2.png) [@d-date](https://forums.swift.org/u/d-date)\
**Post date:** [January 27, 2018, 3:56am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/10 "2018-01-27T03:56:27Z")

</div>

Thank you Nate, I think it's more practical and frequency usecase. I believe `compactMapValues` is useful for us.

Then, what should I do next? I have a chance to write proposal, right? Anything idea do you have?

---

<div class="post-metadata">

**Author:** ![Jon\_Hull](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_hull/32/678_2.png) [@Jon\_Hull](https://forums.swift.org/u/Jon_Hull)\
**Post date:** [January 30, 2018, 1:05am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/11 "2018-01-30T01:05:10Z")

</div>

How about something like this as the implementation?

```
extension Dictionary {
    func compactMapValues<U>(_ transform:(Dictionary.Value)throws->U?)rethrows -> [Dictionary.Key:U] {
        var result:[Key:U] = [:]
        for (k,v) in self {
            if let newValue = try transform(v) {
                result[k] = newValue
            }
        }
        return result
    }
}

```

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [January 30, 2018, 4:29pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/12 "2018-01-30T16:29:53Z")

</div>

> [@d-date](#):
>
> Then, what should I do next? I have a chance to write proposal, right? Anything idea do you have?

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.

---

<div class="post-metadata">

**Author:** ![d-date](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/d-date/32/19682_2.png) [@d-date](https://forums.swift.org/u/d-date)\
**Post date:** [January 31, 2018, 6:38am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/13 "2018-01-31T06:38:38Z")

</div>

OK! I'll try it! Thank you for you advise!

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [February 9, 2018, 2:44pm UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/14 "2018-02-09T14:44:30Z")

</div>

I noticed the Swift Package Manager also defines its own version of `compactMapValues`:

[https://github.com/apple/swift-package-manager/blob/master/Sources/Basic/DictionaryExtensions.swift#L30-L38](https://github.com/apple/swift-package-manager/blob/master/Sources/Basic/DictionaryExtensions.swift#L30-L38)

---

<div class="post-metadata">

**Author:** ![d-date](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/d-date/32/19682_2.png) [@d-date](https://forums.swift.org/u/d-date)\
**Post date:** [February 10, 2018, 10:42am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/15 "2018-02-10T10:42:55Z")

</div>

Thank you lorentey!

I test performance this with my implementation as below

```swift
extension Dictionary {
    public func compactMapValues<T>(_ transform: (Value) throws -> T?) rethrows -> [Key: T] {
        return try self.reduce(into: [Key: T](), { (result, x) in
            if let value = try transform(x.value) {
                result[x.key] = value
            }
        })
    }
}

```

Then I found latter is more faster so that I'll implement by mine and try to fix SPM too.

---

<div class="post-metadata">

**Author:** ![d-date](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/d-date/32/19682_2.png) [@d-date](https://forums.swift.org/u/d-date)\
**Post date:** [February 11, 2018, 9:01am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/16 "2018-02-11T09:01:14Z")

</div>

I posted the [proposal](https://github.com/apple/swift-evolution/pull/787).

Thank you for your helping!!

---

<div class="post-metadata">

**Author:** ![hagi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hagi/32/1224_2.png) [@hagi](https://forums.swift.org/u/hagi)\
**Post date:** [March 8, 2018, 9:33am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/17 "2018-03-08T09:33:47Z")

</div>

I like the addition a lot, thanks for pushing this forward.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [March 20, 2018, 11:44am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/18 "2018-03-20T11:44:15Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![d-date](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/d-date/32/19682_2.png) [@d-date](https://forums.swift.org/u/d-date)\
**Post date:** [March 20, 2018, 11:54am UTC](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741/19 "2018-03-20T11:54:27Z")

</div>

Now that the proposal and implementation was approved by @lorentey. [https://github.com/apple/swift/pull/15017](https://github.com/apple/swift/pull/15017)

I cannot wait for starting evolution discussion!
