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
是一个字典,其中key
是user id
,value
是user value
。
要通过id
访问用户,您现在只需编写
let user = dict["123"]
更新 #1:一般方法
给定一个给定类型Element
的数组,以及一个确定Element
的key
的闭包,以下泛型函数将生成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:数组到字典?的主要内容,如果未能解决你的问题,请参考以下文章