Initializing a Dictionary(uniqueKeysWithValues:) from KeyValuePairs - swift

struct Data {
let storage: [String: Int]
init(_ pairs: KeyValuePairs<String, Int>) {
storage = Dictionary(uniqueKeysWithValues: pairs)
}
}
Compilation error:
Initializer 'init(uniqueKeysWithValues:)' requires the types
'KeyValuePairs<String, Int>.Element' (aka '(key: String, value: Int)')
and '(String, Int)' be equivalent
What can be more natural than to initialize Dictionary(uniqueKeysWithValues:) with KeyValuePairs!? It's just ridiculous that it's impossible to do straightforwardly!
requires '(key: String, value: Int)' and '(String, Int)' be equivalent they are equivalent!

It's because KeyValuePairs.Element has labels.
type(of: (key: "", value: 0)) == type(of: ("", 0)) // false
You need another overload or just an in-place removal of the labels.
public extension Dictionary {
/// Creates a new dictionary from the key-value pairs in the given sequence.
///
/// - Parameter keysAndValues: A sequence of key-value pairs to use for
/// the new dictionary. Every key in `keysAndValues` must be unique.
/// - Returns: A new dictionary initialized with the elements of
/// `keysAndValues`.
/// - Precondition: The sequence must not have duplicate keys.
#inlinable init<Elements: Sequence>(uniqueKeysWithValues keysAndValues: Elements)
where Elements.Element == Element {
self.init(
uniqueKeysWithValues: keysAndValues.map { ($0, $1) }
)
}
}
XCTAssertEqual(
Dictionary(
uniqueKeysWithValues: ["🍐": "πŸͺ‚", "πŸ‘―β€β™€οΈ": "πŸ‘―β€β™‚οΈ"] as KeyValuePairs
),
.init(
uniqueKeysWithValues: [("🍐", "πŸͺ‚"), ("πŸ‘―β€β™€οΈ", "πŸ‘―β€β™‚οΈ")]
)
)

Since Swift is not supporting Splatting (yet), this is how you can achieve it:
struct Data {
let storage: [String: Int]
init(_ pairs: KeyValuePairs<String, Int>) {
storage = Dictionary(uniqueKeysWithValues: pairs.reduce(into: [String: Int]()) { $0[$1.0] = $1.1 } .map { $0 })
}
}
Usages:
let data = Data(KeyValuePairs(dictionaryLiteral: ("a", 1), ("b", 2)))

You may also use reduce:
storage = pairs.reduce(into: [String: Int]()) { $0[$1.key] = $1.value }
This will work even if you have duplicate keys in pairs (currently it takes the last item from duplicates:
let data = Data(["a": 1, "b": 2, "a": 3] as KeyValuePairs)
print(data.storage)
// ["a": 3, "b": 2]
If you need it to take the first item you can do:
storage = pairs.reduce(into: [String: Int]()) { $0[$1.key] = $0[$1.key] ?? $1.value }
(or shorter)
storage = pairs.reduce(into: [String: Int]()) { $0[$1.0] = $0[$1.0] ?? $1.1 }
This prints:
let data = Data(["a": 1, "b": 2, "a": 3] as KeyValuePairs)
print(data.storage)
// ["a": 1, "b": 2]

β€’ Fix:
storage = Dictionary(uniqueKeysWithValues: Array(pairs))
β€’ Why:
The method you are trying to use is:
public init<S>(uniqueKeysWithValues keysAndValues: S) where S : Sequence, S.Element == (Key, Value)
And it's missing the S.Element == (Key, Value) compliance.
β€’ A little further:
Note that if you have duplicate keys, you'll have an error. You can use then:
storage = Dictionary(Array(paris)) { _, new in
return new //if you want to keep the last "key" value, or the reverse
}
β€’ Other possibilities :
convert [(key: String, value: String)] in [String:String]

Related

How to convert Array to Dictionary while keeping the same order Swift

Hey I have been looking at some really good question on here. About how to convert a Array to a dictionary but the problem is that is doesn't keep the same order Example:
list = ["test", "test2", "test3"]
outPut:
listNewFromExt = ["test": "test","test3": "test3", "test2": "test2"]
Basically test3 is being switched places with test2
Code:
let listNewFromExt = list.toDictionary{($0, $0)}
extension Sequence {
public func toDictionary<K: Hashable, V>(_ selector: (Iterator.Element) throws -> (K, V)?) rethrows -> [K: V] {
var dict = [K: V]()
for element in self {
if let (key, value) = try selector(element) {
dict[key] = value
}
}
return dict
}
}
Also if you could tell me how to just make the .values "nil" instead of a copy of the key that would be great lol.
In Swift, a Dictionary is an unordered collection by design. Therefore you can't keep any order to it after migrating from an Array.
If you want your values to be nil, just use
let dict = Dictionary<Int, Any?>(uniqueKeysWithValues: [1, 2, 3].map { ($0, nil) })
This evaluates to [2: nil, 3: nil, 1: nil]
If you want some sort of sorting (no pun intended), you may convert the dict to a sorted tuple array:
let sortedTupleArray = dict.sorted { $0.key > $1.key }
This evaluates to [(key: 3, value: nil), (key: 2, value: nil), (key: 1, value: nil)]

How to check if value type is Dictionary<String, Any>

I created a function in Swift that loop through a dynamic dictionary, however when I try to check if the value is a Dictionary type the type comparison condition always fails, in fact XCode raises the following warning as hint:
Cast from '(key: String, value: Any)' to unrelated type 'Dictionary<String, Any>' always fails.
I am not trying to cast any value, I just want to check if the variable value has the type Dictionary.
This is my code:
func readNode(node: Dictionary<String, Any>, level: Int)
{
// Print spaces
for _ in 0 ... level
{
print(" ")
}
for (key, val) in node.enumerated()
{
// The following condition is always false (here is the issue)
if val is Dictionary<String, Any> {
print("Key \(key):")
readNode(node: val, level: (level + 1) * 2)
}
else
{
print("Key \(key): \(val)")
}
}
}
var mydict = Dictionary<String, Any>()
mydict = ["subindex2": 2, "subindex3": 3, "subindex4": ["whatever": "extra"]]
readNode(node: mydict, level: 0)
I am using Swift 3.0.1.
enumerated() creates a sequence of consecutive Ints starting from 0, paired with the elements of the sequence you call it on. That's not what you want – you just want to iterate over a given dictionary's key-value pairs. So just remove .enumerated(), and iterate over the dictionary directly.
You'll also want to employ conditional type-casting rather than a simple is check, allowing val to be statically typed as [String : Any] in the success branch (otherwise you won't be able to pass it back into readNode(node:level:)).
// ...
for (key, val) in node {
if let val = val as? [String : Any] {
print("Key \(key):")
readNode(node: val, level: (level + 1) * 2)
} else {
print("Key \(key): \(val)")
}
}
// ...

How to check if two [String: Any] are identical?

Is there any way to check if two [String: Any] are identical ?
let actual: [[String: Any]] = [
["id": 12345, "name": "Rahul Katariya"],
["id": 12346, "name": "Aar Kay"]
]
var expected: [[String: Any]]!
if actual == expected {
print("Equal")
}
Basically i want Dictionary to conform to Equatable protocol in Swift 3.
For Xcode 7.3, swift 2.2
A dictionary is of type : [String:AnyObject] or simply put NSDictionary
let actual: [String: AnyObject] = ["id": 12345, "name": "Rahul Katariya"]
var expected: [String: AnyObject] = ["id": 12346, "name": "Aar Kay"]
print(NSDictionary(dictionary: actual).isEqualToDictionary(expected))//False
For Xcode 8.beta 6, Swift 3
Dictionary is defined as:
struct Dictionary<Key : Hashable, Value> : Collection, ExpressibleByDictionaryLiteral
NSDictionary has the following convenience initializer:
convenience init(dictionary otherDictionary: [AnyHashable : Any])
So you can use AnyHashable type for Key and Any type for Value
let actual: [String: Any] = ["id": 12345, "name": "Rahul Katariya"]
var expected: [String: Any] = ["id": 12346, "name": "Aar Kay"]
print(NSDictionary(dictionary: actual).isEqual(to: expected))//False
Conformance to Equatable aside; for the exercise you could write your own isEqual function to compare two [T: Any] dictionaries for a subset of (Equatable) types that you know the value wrapped by Any is limited to. By attempted conversion to these types (e.g. in a switch statement, as below), you could compare the dictionary's values (for each given key) one by one after their conversion to these given types. E.g.
// Usable if the 'Any' values in your dict only wraps
// a few different types _that are known to you_.
// Return false also in case value cannot be successfully
// converted to some known type. This might yield a false negative.
extension Dictionary where Value: Any {
func isEqual(to otherDict: [Key: Any],
allPossibleValueTypesAreKnown: Bool = false) -> Bool {
guard allPossibleValueTypesAreKnown &&
self.count == otherDict.count else { return false }
for (k1,v1) in self {
guard let v2 = otherDict[k1] else { return false }
switch (v1, v2) {
case (let v1 as Double, let v2 as Double) : if !(v1.isEqual(to: v2)) { return false }
case (let v1 as Int, let v2 as Int) : if !(v1==v2) { return false }
case (let v1 as String, let v2 as String): if !(v1==v2) { return false }
// ... fill in with types that are known to you to be
// wrapped by the 'Any' in the dictionaries
default: return false
}
}
return true
}
}
Usage:
/* example setup */
var dict1: [String: Any] = ["id": 12345, "name": "Rahul Katariya", "weight": 70.7]
var dict2: [String: Any] = ["id": 12346, "name": "Aar Kay", "weight": 83.1]
/* example usage */
print(dict1.isEqual(to: dict2, allPossibleValueTypesAreKnown: true))
// false
dict2["name"] = "Rahul Katariya"
dict2["weight"] = 70.7
print(dict1.isEqual(to: dict2, allPossibleValueTypesAreKnown: true))
// false
dict2["id"] = 12345
print(dict1.isEqual(to: dict2, allPossibleValueTypesAreKnown: true))
// true
class Foo {}
dict1["id"] = Foo()
dict2["id"] = Foo()
print(dict1.isEqual(to: dict2, allPossibleValueTypesAreKnown: true))
// false! (we haven't implemented this attempted conversion!)
// incompatable keys cause error as expected an intended
let dict3: [Int: Any] = [1:2]
dict1.isEqual(to: dict3)
/* error: cannot convert value of type '[Int : Any]'
to expected argument type '[String : Any]' */
Just note the danger that the as conversion may yield a false positive (true) as it can allow mapping from two different types to a common other type, e.g. slicing away derived class differences when casting two derived class instances to their common parent type:
class Base: Equatable {}
func ==(lhs: Base, rhs: Base) -> Bool { return true }
class DerivedA : Base {
let foo = "foo"
}
class DerivedB : Base {
let bar = 4.2
}
let a = DerivedA()
let b = DerivedB()
switch (a, b) {
case (let a as Base, let b as Base): print(a == b)
default: ()
} // sliced by conversion! prints "true"
If you'd rather like a failed "known types conversion" to return nil (whereas successful conversions will always yield true/false, based on subsequent equality testing), you could extend the above to (the even messier)
// a 'nil' return here would correspond to an invalid call
extension Dictionary where Value: Any {
func isEqual(to otherDict: [Key: Any],
allPossibleValueTypesAreKnown: Bool = false) -> Bool? {
guard allPossibleValueTypesAreKnown else { return nil }
guard self.count == otherDict.count else { return false }
for (k1,v1) in self {
guard let v2 = otherDict[k1] else { return false }
switch (v1, v2) {
case (let v1 as Double, let v2 as Double) : if !(v1.isEqual(to: v2)) { return false }
case (let v1 as Int, let v2 as Int) : if !(v1==v2) { return false }
case (let v1 as String, let v2 as String): if !(v1==v2) { return false }
// ...
case (_ as Double, let v2): if !(v2 is Double) { return false }
case (_, _ as Double): return false
case (_ as Int, let v2): if !(v2 is Int) { return false }
case (_, _ as Int): return false
case (_ as String, let v2): if !(v2 is String) { return false }
case (_, _ as String): return false
default: return nil
}
}
return true
}
}
/* Example as per above will yield (printout):
Optional(false)
Optional(false)
Optional(true)
nil */
Note however that the value by value equality testing above is short-circuited in case of a false hit, which mean that depending on the random order of the non-ordered dictionaries (non-ordered collection), a special case may return nil as well as false, given two non-equal dictionaries. This special case occurs for two dictionary of non-equal values (non-equality for a known type value-value pair) which also hold an value type not included in the attempted casting: if the non-equality of known types is hit first, false will be returned, whereas if a failed conversion is hit first, nil will be returned. Either way, a nil return means the call should be considered invalid, as caller stated that allPossibleValueTypesAreKnown was true (which a failed conversion implies is false).
The type Any is not Equatable in Swift, so any collection types including Any cannot be Equatable.
You can write something like this in Swift 3/Xcode 8 beta 6:
if actual as NSArray == expected as NSArray {
print("Equal")
}
But, as importing id as Any is just introduced in beta 6, so this behaviour may change in the near future.
With Swift 5.5 you can easily cast it to NSDictionary, as it always succeeds:
XCTAssertEqual(actual as NSDictionary, expected as NSDictionary)

Convert dictionary values to a different type in Swift

I have have a dictionary. I would like to go through it and convert the values to a different type. .map{ } would be perfect except this is a dictionary and not an array. So, I found a mapPairs function on stack overflow that should work for dictionaries. Unfortunately I get a conversion error.
extension Dictionary {
// Since Dictionary conforms to CollectionType, and its Element typealias is a (key, value) tuple, that means you ought to be able to do something like this:
//
// result = dict.map { (key, value) in (key, value.uppercaseString) }
//
// However, that won't actually assign to a Dictionary-typed variable. THE MAP METHOD IS DEFINED TO ALWAYS RETURN AN ARRAY (THE [T]), even for other types like dictionaries. If you write a constructor that'll turn an array of two-tuples into a Dictionary and all will be right with the world:
// Now you can do this:
// result = Dictionary(dict.map { (key, value) in (key, value.uppercaseString) })
//
init(_ pairs: [Element]) {
self.init()
for (k, v) in pairs {
self[k] = v
}
}
// You may even want to write a Dictionary-specific version of map just to avoid explicitly calling the constructor. Here I've also included an implementation of filter:
// let testarr = ["foo" : 1, "bar" : 2]
// let result = testarr.mapPairs { (key, value) in (key, value * 2) }
// result["bar"]
func mapPairs<OutKey: Hashable, OutValue>(#noescape transform: Element throws -> (OutKey, OutValue)) rethrows -> [OutKey: OutValue] {
return Dictionary<OutKey, OutValue>(try map(transform))
}
}
var dict1 = ["a" : 1, "b": 2, "c": 3]
let convertedDict: [String: String] = dict1.mapPairs { // ERROR: cannot convert value of type '_ -> (String, Int)' to expected argument type '(String, Int) -> (String, String)'
element -> (String, Int) in
element[0] = String(element.1)
return element
}
In Swift 5 and later:
let originalDict: [TypeA: TypeB] = /* */
let convertedDict: [TypeA: TypeC] = originalDict.mapValues { /* conversion here */ }
Example:
let integerDict: [String: Int] = ["a": 1, "b": 2]
let doubleDict: [String: Double] = integerDict.mapValues(Double.init)
print(doubleDict) // ["a": 1.0, "b": 2.0]
If you want to change a dict of [String: Int] to [String: String], you can pretty much do the same as my previous answer:
let dict1 = ["a" : 1, "b": 2, "c": 3]
var dict2 = [String: String]()
dict1.forEach { dict2[$0.0] = String($0.1) }
print("\(dict2.dynamicType): \(dict2)")
Output:
Dictionary<String, String>: ["b": "2", "a": "1", "c": "3"]
I don't know if this might help, but since Swift 4.2 there is a new operator called mapValues(_:) (https://developer.apple.com/documentation/swift/dictionary/2995348-mapvalues), which would transform the result you are looking for to:
let convertedDict = dict1.mapValues { String($0) }
As the example given by the method block, you should use mapPairs like this:
let convertedDict: [String: String] = dict1.mapPairs { (key, value) in
(key, String(value))
}
Note, since Swift supports implicit inference, you don't need explicitly return.

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

I'd like to map a function on all keys in the dictionary. I was hoping something like the following would work, but filter cannot be applied to dictionary directly. What's the cleanest way of achieving this?
In this example, I'm trying to increment each value by 1. However this is incidental for the example - the main purpose is to figure out how to apply map() to a dictionary.
var d = ["foo" : 1, "bar" : 2]
d.map() {
$0.1 += 1
}
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!
With Swift 5, you can use one of the five following snippets in order to solve your problem.
#1. Using Dictionary mapValues(_:) method
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
let newDictionary = dictionary.mapValues { value in
return value + 1
}
//let newDictionary = dictionary.mapValues { $0 + 1 } // also works
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
#2. Using Dictionary map method and init(uniqueKeysWithValues:) initializer
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
let tupleArray = dictionary.map { (key: String, value: Int) in
return (key, value + 1)
}
//let tupleArray = dictionary.map { ($0, $1 + 1) } // also works
let newDictionary = Dictionary(uniqueKeysWithValues: tupleArray)
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
#3. Using Dictionary reduce(_:_:) method or reduce(into:_:) method
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
let newDictionary = dictionary.reduce([:]) { (partialResult: [String: Int], tuple: (key: String, value: Int)) in
var result = partialResult
result[tuple.key] = tuple.value + 1
return result
}
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
let newDictionary = dictionary.reduce(into: [:]) { (result: inout [String: Int], tuple: (key: String, value: Int)) in
result[tuple.key] = tuple.value + 1
}
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
#4. Using Dictionary subscript(_:default:) subscript
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
var newDictionary = [String: Int]()
for (key, value) in dictionary {
newDictionary[key, default: value] += 1
}
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
#5. Using Dictionary subscript(_:) subscript
let dictionary = ["foo": 1, "bar": 2, "baz": 5]
var newDictionary = [String: Int]()
for (key, value) in dictionary {
newDictionary[key] = value + 1
}
print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]
While most of the answers here focus on how to map the entire dictionary (keys and values), the question really only wanted to map the values. This is an important distinction since mapping values allows you to guarantee the same number of entries, whereas mapping both key and value might result in duplicate keys.
Here’s an extension, mapValues, that allows you to map just the values. Note it also extends dictionary with an init from a sequence of key/value pairs, which is a bit more general than initializing it from an array:
extension Dictionary {
init<S: SequenceType where S.Generator.Element == Element>
(_ seq: S) {
self.init()
for (k,v) in seq {
self[k] = v
}
}
func mapValues<T>(transform: Value->T) -> Dictionary<Key,T> {
return Dictionary<Key,T>(zip(self.keys, self.values.map(transform)))
}
}
The cleanest way is to just add map to Dictionary:
extension Dictionary {
mutating func map(transform: (key:KeyType, value:ValueType) -> (newValue:ValueType)) {
for key in self.keys {
var newValue = transform(key: key, value: self[key]!)
self.updateValue(newValue, forKey: key)
}
}
}
Checking that it works:
var dic = ["a": 50, "b": 60, "c": 70]
dic.map { $0.1 + 1 }
println(dic)
dic.map { (key, value) in
if key == "a" {
return value
} else {
return value * 2
}
}
println(dic)
Output:
[c: 71, a: 51, b: 61]
[c: 142, a: 51, b: 122]
You can also use reduce instead of map. reduce is capable of doing anything map can do and more!
let oldDict = ["old1": 1, "old2":2]
let newDict = reduce(oldDict, [String:Int]()) { dict, pair in
var d = dict
d["new\(pair.1)"] = pair.1
return d
}
println(newDict) // ["new1": 1, "new2": 2]
It would be fairly easy to wrap this in an extension, but even without the extension it lets you do what you want with one function call.
Swift 5
map function of dictionary comes with this syntax.
dictData.map(transform: ((key: String, value: String)) throws -> T)
you can just set closure values to this
var dictData: [String: String] = [
"key_1": "test 1",
"key_2": "test 2",
"key_3": "test 3",
"key_4": "test 4",
"key_5": "test 5",
]
dictData.map { (key, value) in
// Operations on values or key
}
It may be give this warning Result of call to 'map' is unused
Then just set _ = before variable.
May be it will help you
Thank you.
It turns out you can do this. What you have to do is create an array from the MapCollectionView<Dictionary<KeyType, ValueType>, KeyType> returned from the dictionaries keys method. (Info here) You can then map this array, and pass the updated values back to the dictionary.
var dictionary = ["foo" : 1, "bar" : 2]
Array(dictionary.keys).map() {
dictionary.updateValue(dictionary[$0]! + 1, forKey: $0)
}
dictionary
I was looking for a way to map a dictionary right into a typed Array with custom objects. Found the solution in this extension:
extension Dictionary {
func mapKeys<U> (transform: Key -> U) -> Array<U> {
var results: Array<U> = []
for k in self.keys {
results.append(transform(k))
}
return results
}
func mapValues<U> (transform: Value -> U) -> Array<U> {
var results: Array<U> = []
for v in self.values {
results.append(transform(v))
}
return results
}
func map<U> (transform: Value -> U) -> Array<U> {
return self.mapValues(transform)
}
func map<U> (transform: (Key, Value) -> U) -> Array<U> {
var results: Array<U> = []
for k in self.keys {
results.append(transform(k as Key, self[ k ]! as Value))
}
return results
}
func map<K: Hashable, V> (transform: (Key, Value) -> (K, V)) -> Dictionary<K, V> {
var results: Dictionary<K, V> = [:]
for k in self.keys {
if let value = self[ k ] {
let (u, w) = transform(k, value)
results.updateValue(w, forKey: u)
}
}
return results
}
}
Using it as followed:
self.values = values.map({ (key:String, value:NSNumber) -> VDLFilterValue in
return VDLFilterValue(name: key, amount: value)
})
Swift 3
I try an easy way in Swift 3.
I want to map [String: String?] to [String : String], I use forEach instead of map or flat map.
let oldDict = ["key0": "val0", "key1": nil, "key1": "val2","key2": nil]
var newDict = [String: String]()
oldDict.forEach { (source: (key: String, value: String?)) in
if let value = source.value{
newDict[source.key] = value
}
}
According to the Swift Standard Library Reference, map is a function of arrays. Not for dictionaries.
But you could iterate your dictionary to modify the keys:
var d = ["foo" : 1, "bar" : 2]
for (name, key) in d {
d[name] = d[name]! + 1
}
I you're only trying to map the values (potentially changing their type), include this extension:
extension Dictionary {
func valuesMapped<T>(_ transform: (Value) -> T) -> [Key: T] {
var newDict = [Key: T]()
for (key, value) in self {
newDict[key] = transform(value)
}
return newDict
}
}
Given you have this dictionary:
let intsDict = ["One": 1, "Two": 2, "Three": 3]
Single-line value transformation then looks like this:
let stringsDict = intsDict.valuesMapped { String($0 * 2) }
// => ["One": "2", "Three": "6", "Two": "4"]
Multi-line value transformation then looks like this:
let complexStringsDict = intsDict.valuesMapped { (value: Int) -> String in
let calculationResult = (value * 3 + 7) % 5
return String("Complex number #\(calculationResult)")
}
// => ["One": "Complex number #0", "Three": ...
Another approach is to map to a dictionary and reduce, where functions keyTransform and valueTransform are functions.
let dictionary = ["a": 1, "b": 2, "c": 3]
func keyTransform(key: String) -> Int {
return Int(key.unicodeScalars.first!.value)
}
func valueTransform(value: Int) -> String {
return String(value)
}
dictionary.map { (key, value) in
[keyTransform(key): valueTransform(value)]
}.reduce([Int:String]()) { memo, element in
var m = memo
for (k, v) in element {
m.updateValue(v, forKey: k)
}
return m
}
Swift 3 I used this,
func mapDict(dict:[String:Any])->[String:String]{
var updatedDict:[String:String] = [:]
for key in dict.keys{
if let value = dict[key]{
updatedDict[key] = String(describing: value)
}
}
return updatedDict
}
Usage:
let dict:[String:Any] = ["MyKey":1]
let mappedDict:[String:String] = mapDict(dict: dict)
Ref
Swift 3
Usage:
let value = ["a": "AAA", "b": "BBB", "c": "CCC"]
value.transformed { ($1, $0) } // ["BBB": "b", "CCC": "c", "AAA": "a"]
Extension:
extension Dictionary {
func transformed(closure: (Key, Value) -> (Key, Value)?) -> [Key: Value] {
var dict = [Key: Value]()
for key in keys {
guard
let value = self[key],
let keyValue = closure(key, value)
else { continue }
dict[keyValue.0] = keyValue.1
}
return dict
}
}