[swift-evolution] [Proposal] mapValues
Dave Abrahams
dabrahams at apple.com
Thu Apr 14 11:23:19 CDT 2016
on Wed Apr 13 2016, Andrew Bennett <cacoyi-AT-gmail.com> wrote:
> I'm wondering if `Dictionary<String,Int>.Element`should be `Value`,
> instead of `(Key,Value)`.
I think(?) it's fairly unprecedented. Python sets the
*opposite* precedent, FWIW (a dict is a sequence of its keys).
One possible upside is that Dictionary becomes a MutableCollection.
> With an array you can do this:
>
> for (key, value) in ["a", "b", "c"].enumerate() {
>
> print(key, value) // 0 a, 1 b, 2 c
>
> }
>
> Shouldn't the equivalent for a dictionary be this?
>
> for (key, value) in ["a": 0, "b": 1, "c": 2].enumerate() {
>
> print(key, value) // a 0, b 1, c 2
>
> }
Not unless you can come up with a generic definition of the semantics of
enumerate() that allows the first element of the pair to be the index in
some cases and the key in others. Right now, enumerate() works on
sequences, which you would probably have to drop. Also, don't forget,
Dictionaries still need indices distinct from their keys, because you
shouldn't have to do a hash lookup just to advance to the next element.
> Not this:
>
> for (key, value) in ["a": 0, "b": 1, "c": 2] {
>
> print(key, value) // a 0, b 1, c 2
>
> }
>
> Presumably the old syntax would produce only values:
>
> for value in ["a": 0, "b": 1, "c": 2] {
>
> print(value) // 0, 1, 2
>
> }
>
> You can still iterate the keys like this:
>
> for key in ["a": 0, "b": 1, "c": 2].keys {
>
> print(key) // a, b, c
>
> }
To get them both, I suppose you'd write zip(d.keys, d.values).
> I think I understand the reasoning for Dictionary the way it is, but I
> think it only provides consistency to the implementation, not the
> interface.
Dictionary was not defined the way it is for implementation reasons. In
fact, the keys and values are currently stored in separate arrays. You
are suggesting a different conceptual model for dictionaries, and it
might even be an improvement, but that doesn't mean the current design
is implementation-driven.
> The result of all this being that Dictionary is more consistent (IMO), and map
> would work on the values of all collection types, not a mix of values and
> key/value pairs.
>
> What do you think?
It's an interesting idea, worthy of consideration. I suspect there are
probably lots more arguments to be made here, that haven't occurred to
either of us yet, and maybe some serious downsides. I hope to hear more
about it from others.
> On Thu, Apr 14, 2016 at 9:07 AM, Dave Abrahams via swift-evolution
> <swift-evolution at swift.org> wrote:
>
> 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
>
> _______________________________________________
> 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