Opposite Swift `zip` - tuple into two arrays

I have an array of key-value pairs:

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

I am breaking it into two arrays, for example:

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

In fact, the opposite zipis that I turn an array of tuples into two arrays.

But I do not like the fact that I call maptwice because it means that I iterate over the array twice. However, I also do not want to declare two target arrays in advance as empty arrays and a loop once when added, for example. a forEach. Is there any wonderful idiotic Swifty to unpack my array of tuples into two arrays?

+4
source share
3 answers

In Swift 4, you can use reduce(into:):

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

:

, , . forEach.

, , . , ( , ). , , reduce, dual-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)
    }
}

, , , .

+5

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)

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)
        }
    }
}
+5

, , : reduce:

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

It would be much more beautiful without specifying the initial values, which add a lot of noise and make the code difficult.

The generated one already looks a little cleaner:

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)
+1
source

Source: https://habr.com/ru/post/1685042/


All Articles