Swift 3:数组到字典?

Posted

技术标签:

【中文标题】Swift 3:数组到字典?【英文标题】:Swift 3: Array to Dictionary? 【发布时间】:2017-02-09 00:08:56 【问题描述】:

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

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


2019 年注意事项。现在,这只是 Swift 5 的内置uniqueKeysWithValues 和类似调用。

【问题讨论】:

在数组中你只有字典中的值你有键值。应该是什么键? So I need to create Dictionary 但关键是什么? 你最初是如何得到一个数组的?如果有可以用作键的元素的严格格式,那应该不会太难。你能不能给我们看看数组的一部分? 我想我需要一个闭包来传递密钥,例如班级的成员。为了简单起见,我的第一次尝试是字符串键。 数组中所有元素的键是否唯一?如果是这样,它将与您的数组具有相同的计数,那么您为什么需要字典? 【参考方案1】:

斯威夫特 5

extension Array 

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

【讨论】:

【参考方案2】:
let pills = ["12", "34", "45", "67"]
let kk = Dictionary(uniqueKeysWithValues: pills.map ($0, "number") )

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

swift5 swift4

【讨论】:

完美解决方案!【参考方案3】:

兼容 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"]

重要:

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

下面的代码会使应用崩溃:

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

与:

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

【讨论】:

【参考方案4】:

是这样吗(在 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)]
let firstValues = Dictionary(pairsWithDuplicateKeys, uniquingKeysWith:  (first, _) in first )
let lastValues = Dictionary(pairsWithDuplicateKeys, uniquingKeysWith:  (_, last) in last )
print(firstValues)

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

print(lastValues)

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

【讨论】:

@ShivamPokhriyal - 有一个变体 uniquingKeysWith 用于可能存在欺骗的情况! @Fattie 感谢您提供信息。虽然,我现在不能尝试。无论如何,谢谢! 你应该使用.lazy.map来防止中间复制和数组分配。【参考方案5】:

在 Swift 4 上,您可以通过使用 Dictionary's grouping:by: initializer

来实现这一点

例如: 你有一个名为 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]] 

source

但请注意,生成的 Dictionary "dictionary" 属于类型

[String : [A]]

不是类型的

[String : A]

如您所料。 (使用#uniqueKeysWithValues实现后者。)

【讨论】:

这是最好的正确答案。所有其他答案都没有抓住重点。当然,可以遍历数组并将值复制到字典中。关键是使用 functioal 构造来做到这一点,它可能具有懒惰的额外好处。 完美答案。 @SteveKuo 它比其他垃圾答案要好,但这是错误的:) 您只需使用 uniqueKeysWithValues ,仅此而已。 grouping#by 为您提供 类的数组 作为每个值。您只需使用uniqueKeysWithValues 将数组转换为字典。【参考方案6】:

如果你想遵循 map 设置的模式并在 swift 中减少,你可以做一些很好的功能,如下所示:

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)]

【讨论】:

【参考方案7】:

以下将数组转换为字典。

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

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

【讨论】:

仅当key 没有重复项时。在这个例子中有:5.【参考方案8】:

简单地做,

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

【讨论】:

【参考方案9】:

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

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)

【讨论】:

【参考方案10】:

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

但是,我试图为我对您问题的解释提供一个解决方案。

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 是一个字典,其中keyuser idvalueuser value

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

let user = dict["123"]

更新 #1:一般方法

给定一个给定类型Element 的数组,以及一个确定Elementkey 的闭包,以下泛型函数将生成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

【讨论】:

是的! :-) 这看起来很棒:使用 reduce。我会接受这个。为了改进,模板最好使用类类型和闭包从未知成员变量传递专家密钥。 请注意,这会在每个迭代步骤中创建一个新字典,因此它可能不是大型数组的最佳性能解决方案。 – 有关使用reduce 进行映射操作的一些想法,请参阅airspeedvelocity.net/2015/08/03/…。 @Peter71:我在Update#1 中添加了您要求的通用功能。 @MartinR:你是对的;我在Update#2 中添加了一种更经典的空间复杂度 O(n) 方法。谢谢。 也谢谢你。它看起来有点类似于 overactors 解决方案,所以我认为我们从双方都达到了最佳代码。太好了!【参考方案11】:

我认为您正在寻找这样的东西:

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
    

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

【讨论】:

是的,你可能是对的。 :-) 看起来很棒,但我是初学者,所以我必须通过它来了解这是如何完成的。非常感谢! @Peter71 我已经添加了一些关于如何使用它的演示,如果您遇到任何问题,请随时提出任何具体问题。 非常感谢。我想我明白了。现在我将测试它的速度。但是有什么副作用??? @Peter71 一些序列只能迭代一次,更多信息请查看Apple's documentation of Sequence。除非您的阵列很大,否则性能应该不是问题。而且很难击败一个好的老式 for 循环(在这种情况下,我认为带有 reduce 的版本无论如何都不是特别优雅)。 数组很大(20.000 个元素),但是任何其他方式都很慢,所以我会尝试一下。而且 .reduce 不是最优的,我相信你。 :-) 我没有这方面的经验。【参考方案12】:

据我了解,您想将Array 转换为Dictionary

在我的例子中,我为Array 创建extension,字典的键将是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

【讨论】:

是的,这可以完成工作,但是“手动”。一切都由单独的代码处理。我正在寻找使用内置函数的通用解决方案(如上所示.reduce)

以上是关于Swift 3:数组到字典?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 swift 3 中使用 Alamofire 制作和发送字典数组(JSON 格式)发送到服务器

使用日期 Swift 3 对字典数组进行排序

Swift 4 通过外键将数组映射到字典

访问字典数组 Swift 3

swift:修改字典中的数组

如何在swift 3中找到数组包含字典的数组索引?