与Swift`zip`相反 - 将元组分成两个数组

时间:2017-09-04 20:55:05

标签: arrays swift tuples

我有一组键值对:

let arr = [(key:"hey", value:["ho"]), (key:"ha", value:["tee", "hee"])]

我将它分成两个数组,如下所示:

let (keys, values) = (arr.map{$0.key}, arr.map{$0.value})

实际上,这与zip相反 - 我将一组元组转换为两个数组。

但我不喜欢我两次调用map这一事实,因为这意味着我会两次循环播放数组。然而,我既不想预先将两个目标数组声明为空数组,而是在追加时循环一次,例如与forEach。是否有一些很棒的Swifty成语可以将我的元组数组解压缩成两个数组?

3 个答案:

答案 0 :(得分:8)

在Swift 4中,您可以使用reduce(into:)

let (keys, values) = arr.reduce(into: ([String](), [[String]]())) {
    $0.0.append($1.key)
    $0.1.append($1.value)
}

你说:

  

然而,我既不想预先将两个目标数组声明为空数组,而是在追加时循环一次,例如与forEach

就个人而言,这正是我要做的。我只想编写一个执行此操作的函数(这样您就不会使用该模式填充代码)。但我认为以下内容比reduce模式更加清晰直观,但不会受到双map方法的低效率的影响。

/// Unzip an `Array` of key/value tuples.
///
/// - Parameter array: `Array` of key/value tuples.
/// - Returns: A tuple with two arrays, an `Array` of keys and an `Array` of values.

func unzip<K, V>(_ array: [(key: K, value: V)]) -> ([K], [V]) {
    var keys = [K]()
    var values = [V]()

    keys.reserveCapacity(array.count)
    values.reserveCapacity(array.count)

    array.forEach { key, value in
        keys.append(key)
        values.append(value)
    }

    return (keys, values)
}

或者,如果您觉得有必要将其设为extension,您也可以这样做:

extension Array {

    /// Unzip an `Array` of key/value tuples.
    ///
    /// - Returns: A tuple with two arrays, an `Array` of keys and an `Array` of values.

    func unzip<K, V>() -> ([K], [V]) where Element == (key: K, value: V) {
        var keys = [K]()
        var values = [V]()

        keys.reserveCapacity(count)
        values.reserveCapacity(count)

        forEach { key, value in
            keys.append(key)
            values.append(value)
        }

        return (keys, values)
    }
}

然后按照你的意愿实现这个,但是当你在一个函数中使用它时,你可以支持清晰度和意图。

答案 1 :(得分:7)

Swift 4

reduce(into:)很棒,但不要忘记reserveCapacity以防止重新分配费用:

extension Array {
    func unzip<T1, T2>() -> ([T1], [T2]) where Element == (T1, T2) {
        var result = ([T1](), [T2]())

        result.0.reserveCapacity(self.count)
        result.1.reserveCapacity(self.count)

        return reduce(into: result) { acc, pair in
            acc.0.append(pair.0)
            acc.1.append(pair.1)
        }
    }
}

在Swift 4之前

我会应用KISS原则:

extension Array {
    func unzip<T1, T2>() -> ([T1], [T2]) where Element == (T1, T2) {
        var result = ([T1](), [T2]())

        result.0.reserveCapacity(self.count)
        result.1.reserveCapacity(self.count)

        for (a, b) in self {
            result.0.append(a)
            result.1.append(b)
        }

        return result
    }
}

let arr = [
    (key: "hey", value: ["ho"]),
    (key: "ha",  value: ["tee", "hee"])
]

let unzipped = (arr as [(String, [String])]).unzip()
print(unzipped)

答案 2 :(得分:1)

不是很漂亮,但我现在唯一能想到的就是:使用reduce

let (keys, values) = arr.reduce(([], [])) { ($0.0.0 + [$0.1.key], $0.0.1 + [$0.1.value]) }

如果不必指定会增加大量噪音的初始值并且不容易使代码变得更漂亮,那将会更漂亮。

认为它已经看起来有点干净了:

func unzip<K,V>(_ array : [(K,V)]) -> ([K], [V]) {
    return array.reduce(([], [])) { ($0.0 + [$1.0], $0.1 + [$1.1])}
}

let (keys, values) = unzip(arr)