[Proposal] mapValues


(Jon Hull) #1

I would really like to see something like the following added to the standard library:

extension Dictionary {
    
    func mapValues<U>(transform:(Key,Value)->U)->[Key:U] {
        var output:[Key:U] = [:]
        for (k,v) in self {
            output[k] = transform(k,v)
        }
        return output
    }
    
}

It comes up enough that I have had to add it to pretty much every one of my projects. I also don’t feel comfortable adding it to my frameworks, since I figure a lot of people are also adding something like this to their projects, and I don’t want to cause a conflict with their version. Prime candidate for the standard library.

I like calling it ‘mapValues' as opposed to providing an override for map, since it makes the specific behavior more clear. I would expect ‘map' to possibly map the keys as well (though there are issues where the new keys overlap). I suppose you could just have a bunch of overrides for map if the compiler becomes good enough at differentiating return types: (Value)->(Value), (Key,Value)->Value, (Key, Value)->(Key,Value)

Thanks,
Jon


(Brent Royal-Gordon) #2

I would really like to see something like the following added to the standard library:

extension Dictionary {
    
    func mapValues<U>(transform:(Key,Value)->U)->[Key:U] {
        var output:[Key:U] = [:]
        for (k,v) in self {
            output[k] = transform(k,v)
        }
        return output
    }
    
}

+1 from me, and +78 from people on Stack Overflow: <http://stackoverflow.com/a/24219069/41222>

···

--
Brent Royal-Gordon
Architechies


(David Sweeris) #3

“map” already "maps the values” so I think something like “transform” might be a little clearer, if we don’t want to just overload “map”. Regardless of what it’s called, though, I’m +1 on the functionality.

- Dave Sweeris

···

On Apr 13, 2016, at 1:41 AM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

I would really like to see something like the following added to the standard library:

extension Dictionary {
    
    func mapValues<U>(transform:(Key,Value)->U)->[Key:U] {
        var output:[Key:U] = [:]
        for (k,v) in self {
            output[k] = transform(k,v)
        }
        return output
    }
    
}

It comes up enough that I have had to add it to pretty much every one of my projects. I also don’t feel comfortable adding it to my frameworks, since I figure a lot of people are also adding something like this to their projects, and I don’t want to cause a conflict with their version. Prime candidate for the standard library.

I like calling it ‘mapValues' as opposed to providing an override for map, since it makes the specific behavior more clear. I would expect ‘map' to possibly map the keys as well (though there are issues where the new keys overlap). I suppose you could just have a bunch of overrides for map if the compiler becomes good enough at differentiating return types: (Value)->(Value), (Key,Value)->Value, (Key, Value)->(Key,Value)

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #4

I agree that we need a way to do this, and was surprised when what I
tried didn't work. This should work:

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

We should have a proposal that makes the constructor work* if we don't
already have one.

I'm inclined against building specialized variants of basic algorithms
into particular collections, though. Could be talked out of it if the
use-case is strong enough.

···

on Tue Apr 12 2016, Jonathan Hull <swift-evolution@swift.org> wrote:

I would really like to see something like the following added to the standard
library:

extension Dictionary {

func mapValues<U>(transform:(Key,Value)->U)->[Key:U] {
var output:[Key:U] = [:]
for (k,v) in self {
output[k] = transform(k,v)
}
return output
}

}

It comes up enough that I have had to add it to pretty much every one of my
projects. I also don’t feel comfortable adding it to my frameworks, since I
figure a lot of people are also adding something like this to their projects,
and I don’t want to cause a conflict with their version. Prime candidate for the
standard library.

I like calling it ‘mapValues' as opposed to providing an override for map, since
it makes the specific behavior more clear. I would expect ‘map' to possibly map
the keys as well (though there are issues where the new keys overlap). I suppose
you could just have a bunch of overrides for map if the compiler becomes good
enough at differentiating return types: (Value)->(Value), (Key,Value)->Value,
(Key, Value)->(Key,Value)

------

* Only question: does this need a label, e.g.

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

  to denote that it's lossy?
--
Dave


(Vladimir) #5

+1 for this. Highly useful method and imo should be implemented for Dictionary.

And what if we need to transform the key?
For ex. we have
var d = ["1" : "abc", "2" : "def"]
we could have such method:
d.mapKeys {k,v -> Int in Int(k)! }
to get:
[2: "abc", 1: "def"]

I.e. I suggest to implement and mapKeys() also. It could be also useful in some situations.

···

On 13.04.2016 9:41, Jonathan Hull via swift-evolution wrote:

I would really like to see something like the following added to the
standard library:

extensionDictionary{

func mapValues<U>(transform:(Key,Value)->U)->[Key:U] {
var output:[Key:U] = [:]
for (k,v) in self {
             output[k] = transform(k,v)
         }
return output
     }

}

It comes up enough that I have had to add it to pretty much every one of my
projects. I also don’t feel comfortable adding it to my frameworks, since
I figure a lot of people are also adding something like this to their
projects, and I don’t want to cause a conflict with their version. Prime
candidate for the standard library.

I like calling it ‘mapValues' as opposed to providing an override for map,
since it makes the specific behavior more clear. I would expect ‘map' to
possibly map the keys as well (though there are issues where the new keys
overlap). I suppose you could just have a bunch of overrides for map if
the compiler becomes good enough at differentiating return types:
(Value)->(Value), (Key,Value)->Value, (Key, Value)->(Key,Value)

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#6

I like the idea of having a mapping method which maps back to the actual type.
What about a more general approach? consider:

mappedSelf(transform: T -> U) -> Self<U>
filteredSelf(includeElement: T -> Bool) -> Self<U>

Which can be added to other collections like Set and sequences like a lazy number sequence.

Best regards
- Maximilian

···

Am 13.04.2016 um 08:41 schrieb Jonathan Hull via swift-evolution <swift-evolution@swift.org>:

I would really like to see something like the following added to the standard library:

extension Dictionary {
    
    func mapValues<U>(transform:(Key,Value)->U)->[Key:U] {
        var output:[Key:U] = [:]
        for (k,v) in self {
            output[k] = transform(k,v)
        }
        return output
    }
    
}

It comes up enough that I have had to add it to pretty much every one of my projects. I also don’t feel comfortable adding it to my frameworks, since I figure a lot of people are also adding something like this to their projects, and I don’t want to cause a conflict with their version. Prime candidate for the standard library.

I like calling it ‘mapValues' as opposed to providing an override for map, since it makes the specific behavior more clear. I would expect ‘map' to possibly map the keys as well (though there are issues where the new keys overlap). I suppose you could just have a bunch of overrides for map if the compiler becomes good enough at differentiating return types: (Value)->(Value), (Key,Value)->Value, (Key, Value)->(Key,Value)

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brent Royal-Gordon) #7

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<OutValue>(_ valueTransform: @noescape Value throws -> OutValue) rethrows -> [Key: OutValue] { … }
    
    func mapKeys<OutKey: Hashable>(_ keyTransform: @noescape Key throws -> OutKey) rethrows -> [OutKey: [Value]] { … }

    // Possibly flatMap variants, too?
  }
  
  extension Dictionary where Value: Sequence {
    func reduceValues<OutValue>(_ 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