[swift-evolution] [Proposal] mapValues

Dave Abrahams dabrahams at apple.com
Wed Apr 13 18:07:44 CDT 2016


on Wed Apr 13 2016, Nate Cook <swift-evolution at swift.org> wrote:

>     On Apr 13, 2016, at 12:02 PM, Jacob Bandes-Storch via swift-evolution
>     <swift-evolution at swift.org> wrote:
>
>     To adhere to the API Design Guidelines, I think it should be named
>     "mappingValues", right?
>
>     On Wed, Apr 13, 2016 at 4:23 AM, Vladimir.S via swift-evolution
>     <swift-evolution at swift.org> wrote:
>
>     As for mapKeys and many values into a single key. I believe we should have a
>         choice - do we expect multiply values for the same key or not. Just like
>         "+" and integer overflow : by default it raises the error, but if "&+" -
>         we expect the overflow. I can imagine situations when it is ok for me to
>         have different values for the same key(if I don't care which of values
>         should be for that key in result dictionary).
>         So my proposal is some additional mapKey(allowMultiplyValues: true)
>         {...} or in any other form/name.
>
> There's a proposal (awaiting merging) to add Dictionary initializers and methods
> that work with key/value pairs. These provide different ways of dealing with the
> duplicate key issue after a call to the regular Collection.map method.
>
>     https://github.com/natecook1000/swift-evolution/blob/natecook-dictionary-merge/proposals/0000-add-sequence-based-init-and-merge-to-dictionary.md

Ah, yes, that reminds me of your answer to the question about whether we
need a “uniquingKeys” label on that init(), which IMO is a good one: no,
we don't need it, because the one without the label traps on duplicate
keys.

> I'd be interested in a `mapValues` or `transformValues` method that would modify
> values in place while leaving keys alone.

Is that enough of an improvement over

   Dictionary(d.lazy.map { (k,v) in (k, transform(v)) })

(once we get that initializer) to make it worth expanding the Dictionary
API?

> Another useful method (that could be used to build mapValues more efficiently)
> would be `Dictionary.updateValue(value: Value, at index: DictionaryIndex)`, so
> you could write:
>
> var dict = ["a": 1, "b": 2, "c": 3]
> if let i = dict.index(where: { $0.value == 3 }) {
> dict.updateValue(100, at: i)
> }
> // dict == ["a": 1, "b": 2, "c": 100]

Indeed it would!

> -Nate
>
>         On 13.04.2016 13:38, Ross O'Brien via swift-evolution wrote:
>
>         +1 on mapValues.
>
>             DictionaryLiteral already throws an exception if it includes
>             duplicate
>             keys, so I'd expect mapKeys to throw an error if multiple source
>             keys
>             mapped to the same destination key.
>
>             On Wed, Apr 13, 2016 at 11:28 AM, Miguel Angel Quinones via
>             swift-evolution
>             <swift-evolution at swift.org
>             <mailto:swift-evolution at swift.org>>
>             wrote:
>
>             I'm +1 for adding mapValues. Very useful functionality and trivial
>             to
>             implement.
>
>             > > I.e. I suggest to implement and mapKeys() also. It could be also
>             useful in some situations.
>             > `mapKeys` is much more dangerous, because you could end up mapping
>             many values into a single key. You kind of need to combine the
>             values somehow. Perhaps:
>             >
>             > extension Dictionary {
>             > func mapValues__(_ valueTransform: @noescape Value throws
>             ->OutValue) rethrows ->[Key: OutValue] { … }
>             >
>             > func mapKeys__(_ keyTransform: @noescape Key throws ->OutKey)
>             rethrows ->[OutKey: [Value]] { … }
>             >
>             > // Possibly flatMap variants, too?
>             > }
>             >
>             > extension Dictionary where Value: Sequence {
>             > func reduceValues__(_ initial: OutValue, combine: @noescape
>             (OutValue, Value.Iterator.Element) throws ->OutValue) rethrows ->
>             [Key:
>             OutValue] {
>             > return mapValues { $0.reduce(initial, combine: combine) }
>             > }
>             > }
>             >
>             > Which you would end up using like this:
>             >
>             > let wordFrequencies: [String: Int] = …
>             > let firstLetterFrequencies: [Character: Int] =
>             wordFrequencies.mapKeys { $0.characters.first! }.reduceValues(0,
>             combine: +)
>             >
>             > --
>             > Brent Royal-Gordon
>             > Architechies
>             >
>             >
>             >
>             >______
>
>             --
>             Miguel Angel Quinones
>
>             _______________________________________________
>             swift-evolution mailing list
>             swift-evolution at swift.org
>             <mailto:swift-evolution at swift.org>
>             https://lists.swift.org/mailman/listinfo/swift-evolution
>
>             _______________________________________________
>             swift-evolution mailing list
>             swift-evolution at swift.org
>             https://lists.swift.org/mailman/listinfo/swift-evolution
>
>         _______________________________________________
>         swift-evolution mailing list
>         swift-evolution at swift.org
>         https://lists.swift.org/mailman/listinfo/swift-evolution
>
>     _______________________________________________
>     swift-evolution mailing list
>     swift-evolution at swift.org
>     https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-- 
Dave



More information about the swift-evolution mailing list