3

I have an dictionary where I would like to transform the values of each item, but leave the original dictionary in tact. This is what I am trying to do:

var someDict = [
  "a": 1,
  "b": 2,
  "c": 3
]

func convert(value: Int) -> [String: Int] {
  return someDict.map({
      item in ????
  }
}

Then I am trying to end up with this:

convert(10) // Result: ["a":10, "b":20, "c":30]
convert(20) // Result: ["a":20, "b":40, "c":60]
convert(5) // Result: ["a":5, "b":10, "c":15]
someDict // Result: ["a":1, "b":2, "c":3]

I'm not clear from the documentation on the syntax for this or if map is the correct approach. Any help or suggestion would be greatly appreciated!

TruMan1
  • 33,665
  • 59
  • 184
  • 335
  • Have a look at http://stackoverflow.com/questions/24116271/whats-the-cleanest-way-of-applying-map-to-a-dictionary-in-swift – Martin R Apr 05 '15 at 18:37
  • Not quite a duplicate – that question is “how do I map the keys” whereas this is “how do I map the values”. There’s quite a significant difference between these questions, since mapping keys means changing the number of entries whereas mapping values does not… also, the values don’t have to be hashable. – Airspeed Velocity Apr 05 '15 at 18:50
  • @AirspeedVelocity: If I understand the referenced question correctly, it is about changing the value for each key in a dictionary (concrete: increment the value for each key by one), and therefore quite similar to this question. Also the accepted answer shows how to map both keys and values (and to me looks similar to your answer: map() is applied to the dictionary, and a new dictionary created from an array of key/value pairs). – Correct me if I am wrong, and I'll reopen the answer! – Martin R Apr 05 '15 at 18:54
  • 1
    @MartinR ah, you’re right – while the question is worded “how do I map the keys of a dictionary” the intention is “how do I map the values of a dictionary”. Will post my answer over there. – Airspeed Velocity Apr 05 '15 at 18:59

1 Answers1

8

There’s no direct way to map the values in a dictionary to create a new dictionary.

You can pass a dictionary itself into map, since it conforms to SequenceType. In the case of dictionaries, their element is a key/value pair:

// double all the values, leaving key unchanged
map(someDict) { (k,v) in (k, v*2) }

But this will result in an array of pairs, rather than a new dictionary. But if you extend Dictionary to have an initializer that takes a sequence of key/value pairs, you could then use this to create a new dictionary:

extension Dictionary {
    init<S: SequenceType where S.Generator.Element == Element>
        (_ seq: S) {
            self.init()
            for (k,v) in seq {
                self[k] = v
            }
    }
}

let mappedDict = Dictionary(map(someDict) { (k,v) in (k, v*2) })
// mappedDict will be [“a”:2, “b”:4, “c”:6]

The other downside of this is that, if you alter the keys, you cannot guarantee the dictionary will not throw away certain values (because they keys may be mapped to duplicates of each other).

But if you are only operating on values, you could define a version of map on the values only of the dictionary, in a similar fashion, and that is guaranteed to maintain the same number of entries (since the keys do not change):

extension Dictionary {
    func mapValues<T>(transform: Value->T) -> Dictionary<Key,T> {
        return Dictionary<Key,T>(zip(self.keys, self.values.map(transform)))
    }
}

let mappedDict = someDict.mapValues { $0 * 2 }

In theory, this means you could do an in-place transformation. However, this is not generally a good practice, it’s better to leave it as creating a new dictionary, which you could always assign to the old variable (note, there’s no version of map on arrays that does in-place alteration, unlike say sort/sorted).

Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
  • The `extension` option gave me the error "Argument labels '(_:)' do not match any available overloads" with Swift 2.2 on `return Dictionary(zip(self.keys, self.values.map(transform)))`. I changed this to loop over the return value of `zip()` and assign each value to each key, one at a time: [Gist](https://gist.github.com/donut/2ba63046c6a1daecf8ea) – donut Mar 09 '16 at 19:34
  • @donut do you have a code snippet for what you did? – Hlung May 29 '17 at 08:06
  • nevermind, I made it https://gist.github.com/hlung/243974186c7f67ab3063fde5cf9d8c12 – Hlung May 29 '17 at 08:34