2015-03-03 65 views
1

我有一個對象數組,每個對象都有一個名稱和一個數量。我想掃描數組,如果有任何對象具有相同的名稱,則將所有這些已合併對象的總數減少爲一個對象。是否可以使用高階函數合併數組中的重複對象?

struct AnObject { 
    var name: String 
    var quantity: UInt 
} 

var anArray = [AnObject(name: "one", quantity: 2), 
       AnObject(name: "two", quantity: 2), 
       AnObject(name: "one", quantity: 2), 
       AnObject(name: "one", quantity: 2), 
       AnObject(name: "two", quantity: 2)] 

// something like:  
return reduce(anArray,(),{ /* some magic */}) 

// should return [{"one":6},{"two",4}] 

這可以使用繁瑣的for循環創建一個新的陣列來完成,但有沒有與高階功能,如.filter,。降低甚至.MAP做的更多的「功能」的方式?

回答

3

可以使用reduce方法到陣列轉換成一個字典,具有名作爲關鍵字和值的數組元素,並且因此填充/更新:

// Pass an empty dictionary as initial value 
anArray.reduce([String:AnObject]()) { 
    // The initial value parameter is immutable, so make a mutable copy 
    var dict = $0 

    if dict[$1.name] != nil { 
     // If the key already exists, update the quantity 
     dict[$1.name]?.quantity += $1.quantity 
    } else { 
     // Otherwise add the element 
     dict[$1.name] = $1 
    } 

    return dict 
}.values.array 

最後一行是values集合並將其轉換爲數組。

要注意的是,如果沒有編譯器優化,此解決方案效率不高,但符合功能原理。效率低下的原因是每次迭代都會創建一個新字典。不過,我認爲編譯器能夠通過避免實際副本來優化進程 - 儘管最好測量執行時間並與傳統基於循環的解決方案的性能進行比較,以確定解決方案是否以及如何比其他解決方案更好。

1

你能做到這樣的:

var compressed = [String:UInt]() 
for obj in anArray as [AnObject] { 
    let name = obj.name 
    let val = compressed[name] ?? 0 
    compressed[name] = obj.quantity + val 
} 
var res = [AnObject]() 
for (key, val) in compressed { 
    res.append(AnObject(name:key, quantity:val)) 
} 
res 
+0

這是一個很好的答案,但不是我問:-) – 2015-03-03 13:32:33

+1

您可以創建一個擴展其行爲就像'.filter'的問題。但是你需要這些循環。 – 2015-03-03 15:10:59

+0

@DanielCreagh爲什麼呢?考慮到你的'anObject'類型和'anArray'數據集,@ ThomasKilian的解決方案與@ Antonio's的結果相同:http://imgur.com/a/newNP – 2016-12-31 04:11:22

1

您可以通過實施Equatable使用find

struct AnObject: Equatable { 
    var name: String 
    var quantity: UInt 
} 
func ==(lhs: AnObject, rhs: AnObject) -> Bool { 
    return lhs.name == rhs.name 
} 
anArray = anArray.reduce([AnObject]()) { 
    var array:[AnObject] = $0 
    if let a = find(array, $1) { 
    array[a].quantity += $1.quantity 
    } else { 
    array.append($1) 
    } 
    return array 
}