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!
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
).
Map dictionary with values-arrays to another dictionary with values-arrays in swift?
let's say we have the following [String: [Int]]:
var dict = ["k1":[-1, -2, 1, 2]]
and we want to remap the dictionary removing all the negative elements in the array:
dict = dict.mapValues { v in v.filter { $0 > 0} }
print(dict) // ["k1":[1,2]]
Swift - Reducing a Dictionary of Arrays to a single array of same type using map/reduce/flatmap
All you need is a single flatMap
:
let result = dict.flatMap { _, values in values }
Swift dictionary map - init in closure
Does your value for the dictionary need to be an optional? In a dictionary, when you assign its key as nil, the entry is deleted.
var params = [String:String?]()
params["lat"] = "40"
params["lon"] = "100"
params["key"] = "hey"
print(params) //result: ["lat": Optional("40"), "lon": Optional("100"), "key": Optional("hey")]
params["key"] = nil
print(params) //result: ["lat": Optional("40"), "lon": Optional("100")]
I suggest using a non optional-value dictionary. I have successfully written the code below:
import UIKit
var params = [String:String]()
params["lat"] = "40"
params["lon"] = "100"
let nsurl = params.map() {NSURLQueryItem.init(name: $0, value: $1)}
print(nsurl)
//Result:
//[<NSURLQueryItem 0x7f8252d29730> {name = lat, value = 40}, <NSURLQueryItem 0x7f8252d29700> {name = lon, value = 100}]
I hope this helps
Related Topics
What Is the Purpose of Willset and Didset in Swift
How to Generate a Random Number in Swift Without Repeating the Previous Random Number
Strange Swift Numbers Type Casting
When Are Argument Labels Required in Swift
Check If 'Any' Value Is Object
Using 'Self' in Class Extension Functions in Swift
Property Initializers Run Before 'Self' Is Available
How to Use Userdefaults With Swiftui
How to Create a Reference Cycle Using Dispatchqueues
Swiftui: How to Make Textfield Become First Responder
Deinit Method Is Never Called - Swift Playground
Swift Dictionary Get Key For Value
Binary Operator * Cannot Be Applied to Operands of Type Int and Double
Express For Loops in Swift With Dynamic Range
How to Get Text to Wrap in a Uilabel (Via Uiviewrepresentable) Without Having a Fixed Width