Swift 3:数组到字典?

问题描述 投票:0回答:13

我有一个大数组,需要通过键(查找)访问它,所以我需要创建字典。 Swift 3.0 中有内置函数可以做到这一点,还是我需要自己编写?

首先,我将需要它用于具有键“String”的类,稍后我可能能够编写通用的模板版本(所有类型的数据和键)。

arrays dictionary filter swift3
13个回答
113
投票

是这样吗(在 Swift 4 中)?

let dict = Dictionary(uniqueKeysWithValues: array.map{ ($0.key, $0) })

注: 正如评论中提到的,如果您有重复的键,使用

uniqueKeysWithValues
会产生致命错误 (
Fatal error: Duplicate values for key: 'your_key':
)。

如果您担心这可能是您的情况,那么您可以使用

init(_:uniquingKeysWith:)
例如

let pairsWithDuplicateKeys = [("a", 1), ("b", 2), ("a", 3), ("b", 4)] // or `let pairsWithDuplicateKeys = array.map{ ($0.key, $0) }`

let firstValues = Dictionary(pairsWithDuplicateKeys, uniquingKeysWith: { (first, _) in first })

print(firstValues)

//prints ["a": 1, "b": 2]

let lastValues = Dictionary(pairsWithDuplicateKeys, uniquingKeysWith: { (_, last) in last })

print(lastValues)

//prints ["a": 3, "b": 4]

62
投票

在 Swift 4 上,您可以通过使用 Dictionary's

grouping:by:
初始化器

来实现此目的

对于前: 您的课程名为 A

class A {

    var name: String

    init(name: String) {
        self.name = name
    }
    // .
    // .
    // .
    // other declations and implementions
}

接下来,您有一个类型为 A

的对象数组
let a1 = A(name: "Joy")
let a2 = A(name: "Ben")
let a3 = A(name: "Boy")
let a4 = A(name: "Toy")
let a5 = A(name: "Tim")

let array = [a1, a2, a3, a4, a5]

假设您想通过按首字母对所有名称进行分组来创建字典。您可以使用 Swifts

Dictionary(grouping:by:)
来实现这一目标

let dictionary = Dictionary(grouping: array, by: { $0.name.first! })
// this will give you a dictionary
// ["J": [a1], "B": [a2, a3], "T": [a4, a5]] 

来源

但请注意,生成的 Dictionary “dictionary” 类型为

[String : [A]]

不是类型

[String : A]

正如你所期望的那样。 (使用

#uniqueKeysWithValues
实现后者。)


57
投票

我想你正在寻找这样的东西:

extension Array {
    public func toDictionary<Key: Hashable>(with selectKey: (Element) -> Key) -> [Key:Element] {
        var dict = [Key:Element]()
        for element in self {
            dict[selectKey(element)] = element
        }
        return dict
    }
}

您现在可以执行以下操作:

struct Person {
    var name: String
    var surname: String
    var identifier: String
}

let arr = [Person(name: "John", surname: "Doe", identifier: "JOD"),
           Person(name: "Jane", surname: "Doe", identifier: "JAD")]
let dict = arr.toDictionary { $0.identifier }

print(dict) // Result: ["JAD": Person(name: "Jane", surname: "Doe", identifier: "JAD"), "JOD": Person(name: "John", surname: "Doe", identifier: "JOD")]

如果您希望代码更通用,您甚至可以在

Sequence
而不是
Array
上添加此扩展:

extension Sequence {
    public func toDictionary<Key: Hashable>(with selectKey: (Iterator.Element) -> Key) -> [Key:Iterator.Element] {
        var dict: [Key:Iterator.Element] = [:]
        for element in self {
            dict[selectKey(element)] = element
        }
        return dict
    }
}

请注意,这会导致序列迭代,并且在某些情况下可能会产生副作用。


20
投票

正如其他人已经说过的,我们需要了解哪些是关键。

但是,我正在尝试为我对你的问题的解释提供解决方案。

struct User {
    let id: String
    let firstName: String
    let lastName: String
}

这里我假设不能存在 2 个具有相同

id
的用户

let users: [User] = ...

let dict = users.reduce([String:User]()) { (result, user) -> [String:User] in
    var result = result
    result[user.id] = user
    return result
}

现在

dict
是一本字典,其中
key
user id
value
user value

要通过其

id
访问用户,您现在只需编写

let user = dict["123"]

更新#1:一般方法

给定给定类型

Element
的数组,以及确定
key
Element
的闭包,以下泛型函数将生成
Dictionary
 类型的 
[Key:Element]

func createIndex<Key, Element>(elms:[Element], extractKey:(Element) -> Key) -> [Key:Element] where Key : Hashable {
    return elms.reduce([Key:Element]()) { (dict, elm) -> [Key:Element] in
        var dict = dict
        dict[extractKey(elm)] = elm
        return dict
    }
}

示例

let users: [User] = [
    User(id: "a0", firstName: "a1", lastName: "a2"),
    User(id: "b0", firstName: "b1", lastName: "b2"),
    User(id: "c0", firstName: "c1", lastName: "c2")
 ]

let dict = createIndex(elms: users) { $0.id }
// ["b0": {id "b0", firstName "b1", lastName "b2"}, "c0": {id "c0", firstName "c1", lastName "c2"}, "a0": {id "a0", firstName "a1", lastName "a2"}]

更新#2

正如 Martin R 所指出的,reduce 将为相关闭包的每次迭代创建一个新字典。这可能会导致巨大的内存消耗。

这是

createIndex
函数的另一个版本,其中空间要求为 O(n),其中 n 是榆树的长度。

func createIndex<Key, Element>(elms:[Element], extractKey:(Element) -> Key) -> [Key:Element] where Key : Hashable {
    var dict = [Key:Element]()
    for elm in elms {
        dict[extractKey(elm)] = elm
    }
    return dict
}

12
投票
let pills = ["12", "34", "45", "67"]
let kk = Dictionary(uniqueKeysWithValues: pills.map{ ($0, "number") })

["12": "number", "67": "number", "34": "number", "45": "number"]

斯威夫特5 斯威夫特4


9
投票

以下将数组转换为字典。

let firstArray = [2,3,4,5,5] 

let dict = Dictionary(firstArray.map { ($0, 1) } , uniquingKeysWith: +)

6
投票

斯威夫特5

extension Array {

    func toDictionary() -> [Int: Element] {
        self.enumerated().reduce(into: [Int: Element]()) { $0[$1.offset] = $1.element }
    }
    
}

4
投票

此扩展适用于所有序列(包括数组),并允许您选择键和值

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
    }
}

示例:

let nameLookup = persons.toDictionary{($0.name, $0)}

3
投票

就简单地做吧

let items = URLComponents(string: "https://im.qq.com?q=13&id=23")!.queryItems!

var dic = [String: Any?]()
items.foreach {
    dic[$0.name] = $0.value
}

reduce
不太合适,

let dic: [String: Any?] = items.reduce([:]) { (result: [String: Any?], item: URLQueryItem) -> [String: Any?] in
   var r = result
   r[item.name] = item.value // will create an copy of result!!!!!!
   return r
}

2
投票

据我从您的问题中了解到,您想将

Array
转换为
Dictionary

在我的例子中,我为

extension
创建
Array
,字典的键将是
Array
的索引。

示例:

var intArray = [2, 3, 5, 3, 2, 1]

extension Array where Element: Any {

    var toDictionary: [Int:Element] {
        var dictionary: [Int:Element] = [:]
        for (index, element) in enumerate() {
            dictionary[index] = element
        }
        return dictionary
    }

}

let dic = intArray.toDictionary

2
投票

兼容 Swift 5 标准库(Xcode 10.2+、iOS 12.2)。

这是初始化器的使用示例

init(uniqueKeysWithValues:)

输入

let array: [String] = Locale.isoRegionCodes
是由字符串表示的ISO31661-2代码的数组。

let countryCodeAndName: [String: String] = Dictionary(uniqueKeysWithValues: Locale.isoRegionCodes.map { ($0, Locale.current.localizedString(forRegionCode: $0) ?? "")} )

返回的字典,将列出以 ISO31661-2 代码作为键、以本地化区域名称作为值的所有区域。

输出:

...
"PL":"Poland"
"DE":"Germany"
"FR":"France"
"ES":"Spain"
...

示例2:

let dictionary: [String: String] = Dictionary(uniqueKeysWithValues: [ ("key1", "value1"), ("key2", "value2")] )

输出:

["key1": "value1", "key2": "value2"]

重要:

前提条件:序列不能有重复的key。

下面的代码将使应用程序崩溃:

let digitWords = ["one", "two", "three", "four", "five", "five"]
let wordToValue = Dictionary(uniqueKeysWithValues: zip(digitWords, 1...6))

与:

致命错误:键值重复:“五个”


1
投票

如果你想遵循 map 设定的模式并快速减少,你可以做一些漂亮且实用的事情,如下所示:

extension Array {
    func keyBy<Key: Hashable>(_ keyFor: (Element) -> Key) -> [Key: Element] {
        var ret = [Key: Element]()
        for item in self{
            ret[keyFor(item)] = item
        }
        return ret
    }
}

用途:

struct Dog {
    let id: Int
}

let dogs = [Dog(id: 1), Dog(id: 2), Dog(id: 3), Dog(id: 4)]
let dogsById = dogs.keyBy({ $0.id }) 
            // [4: Dog(id: 4), 1: Dog(id: 1), 3: Dog(id: 3), 2: Dog(id: 2)]

1
投票

快捷方式:

extension Sequence {
    func toDictionary<Key: Hashable>(with selectKey: (Element) -> Key) -> [Key: Element] {
        reduce(into: [:]) { $0[selectKey($1)] = $1 }
    }
}

// let arr = [Person(id: 1, name: "Alan")]
// arr.toDictionary { $0.id }
// ==
// [1: Person(id: 1, name: "Alan")]
© www.soinside.com 2019 - 2024. All rights reserved.