Map Dictionary Keys to Add Values - Swift

What's the cleanest way of applying map() to a dictionary in Swift?

Swift 4+

Good news! Swift 4 includes a mapValues(_:) method which constructs a copy of a dictionary with the same keys, but different values. It also includes a filter(_:) overload which returns a Dictionary, and init(uniqueKeysWithValues:) and init(_:uniquingKeysWith:) initializers to create a Dictionary from an arbitrary sequence of tuples. That means that, if you want to change both the keys and values, you can say something like:

let newDict = Dictionary(uniqueKeysWithValues:
oldDict.map { key, value in (key.uppercased(), value.lowercased()) })

There are also new APIs for merging dictionaries together, substituting a default value for missing elements, grouping values (converting a collection into a dictionary of arrays, keyed by the result of mapping the collection over some function), and more.

During discussion of the proposal, SE-0165, that introduced these features, I brought up this Stack Overflow answer several times, and I think the sheer number of upvotes helped demonstrate the demand. So thanks for your help making Swift better!

Map Dictionary Keys to add values - Swift

Assuming data is mutable, this should do it:

data.merge(results, uniquingKeysWith: { $0 + $1 })

How to append elements into a dictionary in Swift?

You're using NSDictionary. Unless you explicitly need it to be that type for some reason, I recommend using a Swift dictionary.

You can pass a Swift dictionary to any function expecting NSDictionary without any extra work, because Dictionary<> and NSDictionary seamlessly bridge to each other. The advantage of the native Swift way is that the dictionary uses generic types, so if you define it with Int as the key and String as the value, you cannot mistakenly use keys and values of different types. (The compiler checks the types on your behalf.)

Based on what I see in your code, your dictionary uses Int as the key and String as the value. To create an instance and add an item at a later time you can use this code:

var dict = [1: "abc", 2: "cde"] // dict is of type Dictionary<Int, String>
dict[3] = "efg"

If you later need to assign it to a variable of NSDictionary type, just do an explicit cast:

let nsDict = dict as! NSDictionary

And, as mentioned earlier, if you want to pass it to a function expecting NSDictionary, pass it as-is without any cast or conversion.

Finding the sum of the values of a dictionary with matching keys

You can use flatMap to create one array of tuples and then reduce(into:) to create a dictionary with the keys and the sum of the values per key

let result = dict.values
.flatMap {$0}
.reduce(into: [:]) { $0[$1.key, default: 0] += $1.value }

How to use map to transform a dictionary in Swift?

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

Swift - map dictionary based on keys

Your purpose is not clear enough with seeing your updated example, but if you just want to gather the values for the same key each, you can write something like this:

let data = json["data"] as! [String: [String: Any]]
let result = data["xaxis"]!.map {($0.key, $0.value, data["forecast_numbers"]![$0.key]!, data["actual_numbers"]![$0.key]!)}
print(result) //->[("130", NNN, 4480161, 2451170), ("20", BCD, 985448, 1645125), ("76", BNM, 2499160, 1644789), ("15", ABC, 6397822, 6344454)]

Assuming you have that JSON data in json as [String: AnyObject].

(Also assuming your json never contains some broken data. Seemingly using too much !.)

how to combine duplicate key in dictionary and sum up values in swift?

You can use Dictionary.init<S>(_ keysAndValues: S, uniquingKeysWith combine: (Value, Value) throws -> Value), which takes a Sequence of tuples, storing the key-value pairs and a closure, which defines how to handle duplicate keys.

// Create the key-value pairs
let keysAndValues = revenues.map { ($0.customerName, $0.revenue) }
// Create the dictionary, by adding the values for matching keys
let revenueDict = Dictionary(keysAndValues, uniquingKeysWith: { $0 + $1 })

Test code:

struct RevenueItem {
let customerName: String
let revenue: Int
}

let revenues = [
RevenueItem(customerName: "a", revenue: 1),
RevenueItem(customerName: "a", revenue: 9),
RevenueItem(customerName: "b", revenue: 1)
]

let keysAndValues = revenues.map { ($0.customerName, $0.revenue) }
let revenueDict = Dictionary(keysAndValues, uniquingKeysWith: { $0 + $1 })
revenueDict // ["a": 10, "b": 1]

Swift - how to map array to dictionary values?

The least syntax you can use involves AnyIterator to repeat a value indefinitely.

Dictionary(uniqueKeysWithValues: zip(0...100, AnyIterator { false }))

How to map an array of integers as keys to dictionary values

You can map() each array element to the corresponding value in
the dictionary. If it is guaranteed that all array elements are
present as keys in the dictionary then you can do:

let mysteryDoors = [3, 1, 2]
let doorPrizes = [ 1:"Car", 2: "Vacation", 3: "Gift card"]

let prizes = mysteryDoors.map { doorPrizes[$0]! }
print(prizes) // ["Gift card", "Car", "Vacation"]

To avoid a runtime crash if a key is not present, use

let prizes = mysteryDoors.flatMap { doorPrizes[$0] }

to ignore unknown keys, or

let prizes = mysteryDoors.map { doorPrizes[$0] ?? "" }

to map unknown keys to a default string.



Related Topics



Leave a reply



Submit