2017-09-04 41 views
4

我有鍵 - 值對的數組:對面夫特`zip`的 - 元組分裂成兩個陣列

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成語,用於將我的元組數組解壓縮到兩個數組中?

+4

在斯威夫特4: let(keys,values)= arr.reduce(into:([String](),[[String]]())){$ 0.0.append($ 1.key); $ 0.1.append($ 1.value)}'。 – Rob

+0

...或Swift 4使用較短的初始值:'let(keys,values)= arr。reduce(into:([],[])){$ 0.0.append($ 1.key); $ 0.1.append($ 1.value)}' – vacawama

+2

@vacawama - 是的,但是'keys'和'values'的類型是[Any],因爲它無法成功推斷出正確的類型。 – Rob

回答

5

在斯威夫特4,你可以使用reduce(into:)

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

你說:

但我也不希望事先將兩個目標數組聲明爲空數組,並在追加時循環一次,例如與forEach

就個人而言,這正是我所要做的。我只會寫一個這樣做的函數(這樣你就不會在你的代碼中使用該模式)。但我認爲以下內容比reduce模式更清晰直觀,但不會遭受雙重方法的低效率。

/// 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) 
    } 
} 

實現這個,但是你會喜歡,但是當你擁有它的功能,你可以贊成清晰和意圖。

+0

不要忘記'reserveCapacity'以防止重新分配 – Alexander

+0

@matt - 在WWDC 2017中重新縮減(到:)'[Swift 4中的新增內容](https://developer.apple.com/videos/play/ wwdc2017/402 /),他們指示我們去[Ole Begemann的遊樂場](https://github.com/ole/whats-new-in-swift-4)。那是我遇到它的地方。 – Rob

+0

你也可以將它作爲'Sequence'的擴展;保留'低估的計數'的容量值。 – Hamish

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

我想出了let(keys,values)= arr.reduce(([],[])){($ 0.0 + [$ 1.key],$ 0.1 + [$ 1.value] )}' – vacawama

+5

這可以通過'reduce(into:)'進入Swift 4,它的閉包需要一個'inout'參數,但是正常的Swift 3版本的'reduce'具有*非常差的性能。使用'reduce()'和'+'運算符構建數組,因爲它必須在循環的每次迭代中複製整個數組。 –

+0

@vacawama我*總是*忘記'[]'也適用於這些情況 - 添加了它。而較短的'$ ...'語法對我來說不起作用,但它最終奏效,謝謝。 – luk2302

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) 

斯威夫特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) 
     } 
    } 
}