Swift equivalent to `[NSDictionary initWithObjects: forKeys:]`

假装没事ソ 提交于 2019-12-17 05:11:31

问题


Is there an equivalent for Swift's native Dictionary to [NSDictionary initWithObjects: forKeys:]?

Say I have two arrays with keys and values and want to put them in a dictionary. In Objective-C I'd do it like this:

NSArray *keys = @[@"one", @"two", @"three"];
NSArray *values = @[@1, @2, @3];
NSDictionary *dict = [[NSDictionary alloc] initWithObjects: values forKeys: keys];

Of course I can iterate with a counter through both arrays, use a var dict: [String:Int] and add stuff step by step. But that doesn't seem to be a good solution. Using zip and enumerate are probably better ways of iterating over both at the same time. However this approach means having a mutable dictionary, not an immutable one.

let keys = ["one", "two", "three"]
let values = [1, 2, 3]
// ???
let dict: [String:Int] = ["one":1, "two":2, "three":3] // expected result

回答1:


As of Swift 4 you can create a dictionary directly from a sequence of key/value pairs:

let keys = ["one", "two", "three"]
let values = [1, 2, 3]

let dict = Dictionary(uniqueKeysWithValues: zip(keys, values))

print(dict) // ["one": 1, "three": 3, "two": 2]

This assumes that all keys are different, otherwise it will abort with a runtime exception.

If the keys are not guaranteed to be distinct then you can do

let keys = ["one", "two", "one"]
let values = [1, 2, 3]

let dict = Dictionary(zip(keys, values), uniquingKeysWith: { $1 })

print(dict) // ["one": 3, "two": 2]

The second argument is a closure which determines which value "wins" in the case of duplicate keys.




回答2:


You can simply use the Swift equivalent of initWithObjects:forKeys:

let keys = ["one", "two", "three"]
let values = [1, 2, 3]
var dict = NSDictionary.init(objects: values, forKeys: keys)



回答3:


Working pure Swift solution with structs. Use zip to iterate through your two arrays as a tuple, and then create a dictionary for each key, value in the tuple.

struct SomeStruct {
    var someVal: Int?
}

var keys = [String]()
var values = [SomeStruct]()

for index in 0...5 {
    keys.append(String(index))
    values.append(SomeStruct(someVal: index))
}

var dict = [String : Any]()

for (key, value) in zip(keys, values) {
    dict[key] = value
}

print(dict) // "["4": SomeStruct(someVal: Optional(4)), "2": SomeStruct(someVal: Optional(2)), "1": SomeStruct(someVal: Optional(1)), "5": SomeStruct(someVal: Optional(5)), "0": SomeStruct(someVal: Optional(0)), "3": SomeStruct(someVal: Optional(3))]"

You could also use forEach on zip:

var dict = [String : Any]()
zip(keys, values).forEach { dict[$0.0] = $0.1 }
print(dict) // "["4": SomeStruct(someVal: Optional(4)), "2": SomeStruct(someVal: Optional(2)), "1": SomeStruct(someVal: Optional(1)), "5": SomeStruct(someVal: Optional(5)), "0": SomeStruct(someVal: Optional(0)), "3": SomeStruct(someVal: Optional(3))]\n"



回答4:


A one-liner, using zip and reduce:

let dict = zip(keys, values).reduce([String:Int]()){ var d = $0; d[$1.0] = $1.1; return d }

You can shorten the reduce expression by defining the + operator for a Dictionary and a tuple:

func +<K,V>(lhs: [K:V], rhs: (K, V)) -> [K:V] {
    var result = lhs
    result[rhs.0] = rhs.1
    return result
}

let dict = zip(keys, values).reduce([String:Int](), combine: +)



回答5:


let keys = ["one", "two", "three"]
let values = [1, 2, 3]

func createDict<K:Hashable,V>(keys: [K], values:[V])->[K:V] {

    var dict: [K:V] = [:]

    // add validity checks here by yourself !
    // and return early, or throw an error ...

    keys.enumerate().forEach { (index,element) -> () in
        dict[element] = values[index]
    }
    return dict
}

let dict = createDict(keys, values: values)
// ["one": 1, "three": 3, "two": 2]

let dict2:[Int:Any] = createDict([1,2,3,4,5], values: [true,"two",3.4,5,[1,2,3]])
// [5: [1, 2, 3], 2: "two", 3: 3.4, 1: true, 4: 5]

what is the difference if it is compared to zip solution? hard to say ... for me the zip type annotation is the biggest issue

let a:Zip2Sequence<[Int],[Any]> = zip([1,2,3,4,5], [true,"two",3.4,5,[1,2,3]])
var d:[Int:Any] = [:]
a.forEach { (key, value) -> () in
    d[key] = value
}
print(d)
// [5: [1, 2, 3], 2: "two", 3: 3.4, 1: true, 4: 5]

but enumerate solution is also a little bit quicker



来源:https://stackoverflow.com/questions/34927057/swift-equivalent-to-nsdictionary-initwithobjects-forkeys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!