我有一个对象数组,每个对象都有一个名称和数量。我想扫描数组,如果有任何对象具有相同的名称,则将所有那些已合并的对象的总数减少到一个单个对象。
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,.reduce甚至.map等高阶函数是否有更“实用”的方法呢?
答案 0 :(得分:3)
您可以使用reduce
方法将数组转换为字典,名称为key,数组元素为value,并相应地填充/更新:
// 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 :(得分: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
答案 2 :(得分:1)
您可以通过实施find
来使用Equatable
:
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
}