如何在 Swift 中一行代码解决数组转字典的问题

-

Posted by Yxi on January 5, 2021

前言

持续学习


偷师于大佬

如何在 Swift 中一行代码解决数组转字典的问题

把Model数组转字典。

struct Data {
    var name: String
}

// mock 数据
var dataList = Array(0..<10).map { Data.init(name: "data \($0)") }

将Array以它的`name`为`key`,它本身作为`value`,放到一个数组里。

常规做法1:

var dataMap = Dictionary<String, Data>()

for i in 0..<dataList.count {
    dataMap[dataList[i].name] = dataList[i]
}

常规做法2:

var dataMap = Dictionary<String, Data>()

for data in dataList {
    dataMap[data.name] = data
}

一句话代码:

var dataMap = Dictionary.init(dataList.map { ($0.name, $0) }) { $1 }

解析:

let dataPairList = dataList.map { ($0.name, $0) }
dataMap = Dictionary.init(dataPairList, uniquingKeysWith: { (old, new) in
    new
})
  • 第一行里,把 key 和 value 拆成了键值对(元组),结果就是下面构造方法的第一个参数。
  • 第二行里,构造方法的第二个参数是一个闭包,用来决定当键重复时,用哪个当值。在例子里直接使用新的 model 覆盖旧的。然后直接构造字典。

Dictionary 的这个构造方法的签名是:

    /// Creates a new dictionary from the key-value pairs in the given sequence,
    /// using a combining closure to determine the value for any duplicate keys.
    ///
    /// You use this initializer to create a dictionary when you have a sequence
    /// of key-value tuples that might have duplicate keys. As the dictionary is
    /// built, the initializer calls the `combine` closure with the current and
    /// new values for any duplicate keys. Pass a closure as `combine` that
    /// returns the value to use in the resulting dictionary: The closure can
    /// choose between the two values, combine them to produce a new value, or
    /// even throw an error.
    ///
    /// The following example shows how to choose the first and last values for
    /// any duplicate keys:
    ///
    ///     let pairsWithDuplicateKeys = [("a", 1), ("b", 2), ("a", 3), ("b", 4)]
    ///
    ///     let firstValues = Dictionary(pairsWithDuplicateKeys,
    ///                                  uniquingKeysWith: { (first, _) in first })
    ///     // ["b": 2, "a": 1]
    ///
    ///     let lastValues = Dictionary(pairsWithDuplicateKeys,
    ///                                 uniquingKeysWith: { (_, last) in last })
    ///     // ["b": 4, "a": 3]
    ///
    /// - Parameters:
    ///   - keysAndValues: A sequence of key-value pairs to use for the new
    ///     dictionary.
    ///   - combine: A closure that is called with the values for any duplicate
    ///     keys that are encountered. The closure returns the desired value for
    ///     the final dictionary.
    @inlinable public init<S>(_ keysAndValues: S, uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows where S : Sequence, S.Element == (Key, Value)

这个方法由 Swift 4 引入。

自己动手,丰衣足食

看完大佬的代码,还是有点不解,自己再写一遍,加深记忆

解析代码等价于

let dataPairList = dataList.map { ($0.name, $0) }

// 等价于
let dataPairList = dataList.map { data -> (String, Data) in
      // $0:第一个参数,这里是map中的变量
    let tuple = (data.name, data)
    return tuple
}

// 同理,mock数据
var dataList = Array(0..<10).map { Data.init(name: "data \($0)") }
var dataList = Array(0..<10).map { index in
    Data.init(name: "data \(index)")
}