我该如何从元组数组创建字典?

39

假设我有一个可以被识别的对象数组,并且我想从中创建字典。我可以通过以下方式轻松地从我的数组中获取元组:

let tuples = myArray.map { return ($0.id, $0) }
但我看不到初始化字典以使用元组数组的方法。我漏掉了什么吗?我需要为此功能创建字典扩展(实际上这并不难,但我认为它应该是默认提供的),或者有更简单的方法吗?
这里有扩展的代码。
extension Dictionary
{
    public init (_ arrayOfTuples : Array<(Key, Value)>)
    {
        self.init(minimumCapacity: arrayOfTuples.count)

        for tuple in arrayOfTuples
        {
            self[tuple.0] = tuple.1
        }
    }
}

4
为什么默认应该是这样呢?将元组数组映射到字典没有一般性的方法。在字典中,每个键必须是唯一的;在元组数组中,允许存在任意数量的重复“键”。对于它们应该怎么办呢?重写?忽略? - user28434'mstep
是的,@user28434 是正确的。顺便看一下这个 https://dev59.com/Ll0Z5IYBdhLWcg3wbwCY#31447400 - Irfan
1
是的,@user28434,你说得对,我没有考虑到那个。 - Adamsor
你为什么要首先创建一个元组数组呢?你可以直接使用myArray中的数据创建字典。 - gnasher729
6个回答

65

Swift 4

如果你的元组类型是 (Hashable, String),你可以使用以下方式:

let array = [("key1", "value1"), ("key2", "value2"), ("key3", "value3")]
let dictionary = array.reduce(into: [:]) { $0[$1.0] = $1.1 }
print(dictionary) // ["key1": "value1", "key2": "value2", "key3": "value3"]

3
我不知道为什么这篇回答被踩了,因为这是解决提问者问题最恰当的方法。 - Ilias Karim
1
我 ❤️ 这个答案! - Ashley Mills
这个解决方案默认情况下不起作用。在reduce函数的每次迭代中,累积字典$0是不可变的,因此您需要一种可变的新值来处理它。 - hamsternik

22

Swift 4

创建字典可以使用原生Dictionary的 init 函数:

Dictionary(uniqueKeysWithValues: [("a", 0), ("b", 1)]) 
// ["b": 1, "a": 0]

Dictionary(uniqueKeysWithValues: [("a", 0), ("b", 1), ("b", 2)])
// Fatal error: Duplicate values for key: 'b'

// takes the first match
Dictionary([("a", 0), ("b", 1), ("a", 2)], uniquingKeysWith: { old, _ in old })
// ["b": 1, "a": 0]

// takes the latest match
Dictionary([("a", 0), ("b", 1), ("a", 2)], uniquingKeysWith: { $1 }) 
// ["b": 1, "a": 2]

如果您也想创建快捷方式:

Dictionary([("a", 0), ("b", 1), ("a", 2)]) { $1 } 
// ["b": 1, "a": 2]

8

根据您想要完成的任务,您可以选择以下方式:

let tuples = [(0, "0"), (1, "1"), (1, "2")]
var dictionary = [Int: String]()

选项1:替换现有密钥


该选项是指替换已存在的密钥。
tuples.forEach {
    dictionary[$0.0] = $0.1
}    
print(dictionary) //prints [0: "0", 1: "2"]

选项2:不允许重复键
enum Errors: Error {
    case DuplicatedKeyError
}

do {
    try tuples.forEach {
        guard dictionary.updateValue($0.1, forKey:$0.0) == nil else { throw Errors.DuplicatedKeyError }
    }
    print(dictionary)
} catch {
    print("Error") // prints Error
}

6

通用方法:

/**
 * Converts tuples to dict
 */
func dict<K,V>(_ tuples:[(K,V)])->[K:V]{
    var dict:[K:V] = [K:V]()
    tuples.forEach {dict[$0.0] = $0.1}
    return dict
}

函数式编程更新:

func dict<K,V>(tuples:[(K,V)])->[K:V]{
    return tuples.reduce([:]) {
       var dict:[K:V] = $0
       dict[$1.0] = $1.1   
       return dict
    }
}

1
@hasen 这个有争议。因为您将元组作为参数传递,所以它绝对不应该只是函数。将其静态化明确表示它不能改变所在类的状态。 - Sentry.co
1
它不需要驻留在一个类中,它只是一个函数。Swift 不是 Java。 - hasen
1
@hasen 我猜它是自以为是的,为了简洁起见我会将其更改为func。感谢您的意见。 - Sentry.co
我将这个函数作为字典扩展进行初始化。 - Daniel T.
4
@DanielT. 已经有一个本地方法:init(uniqueKeysWithValues:)(Swift 4) - Sentry.co

3

使用扩展程序改进了 @GitSync 的答案。

extension Array {
    func toDictionary<K,V>() -> [K:V] where Iterator.Element == (K,V) {
        return self.reduce([:]) {
            var dict:[K:V] = $0
            dict[$1.0] = $1.1
            return dict
        }
    }
}

1

更新至@Stefan的答案。

extension Array {

    func toDictionary<K, V>() -> [K: V] where Iterator.Element == (K, V) {
        return Dictionary(uniqueKeysWithValues: self)
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接