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
Swift: Mkannotation Long Title Text
How to Create Generic Closures in Swift
Can't Set @Ibinspectable Computed Property in UIview
Swiftui Remove Transition of Fullscreen Cover
Change Color of Row Programmatically in Watchkit
Swift Getnameinfo Unreliable Results for Ipv6
What Is The Advantage of Closure Stored Property Initialisation
Non-Translucent UItabbar Creates Strange Grey Bar
How to Create Viewcontrollers Without Storyboard and Set One as Delegate of The Other One
How to Use Gpx File for UI Tests Only
Swiftui: Unable to Animate Images
How to Create Nested Dictionary Elements in Swift
Dynamic Links Firebase Function Not Being Called at All Xcode 12
Rlmexception', Reason: 'Attempting to Modify Object Outside of a Write Transaction
Swift 3/4 Dash to Camel Case (Snake to Camelcase)
Swift: Trunc a Floating Number to Show It in a Label
Swift: Simple Dispatchqueue Does Not Run & Notify Correctly
I Am Loading Videos in Avplayer in Collection View But It Repeats Some Cells Data