是否可以使用高阶函数合并数组中的重复对象?
Is it possible to use high order functions to amalgamate duplicate objects in an array?
我有一个对象数组,每个对象都有一个名称和一个数量。我想扫描整个数组,如果有任何对象具有相同的名称,则将所有已合并对象的总数减少到一个对象。
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 循环创建新数组来完成,但是是否有更多 'functional' 方法可以使用高阶函数(如 .filter、.reduce 甚至 .map)来完成?
你可以这样做:
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
可以使用reduce
方法将数组转换为字典,以名称为键,数组元素为值,populate/update相应:
// Pass an empty dictionary as initial value
anArray.reduce([String:AnObject]()) {
// The initial value parameter is immutable, so make a mutable copy
var dict = [=10=]
if dict[.name] != nil {
// If the key already exists, update the quantity
dict[.name]?.quantity += .quantity
} else {
// Otherwise add the element
dict[.name] =
}
return dict
}.values.array
最后一行获取 values
集合并将其转换为数组。
请注意,如果没有编译器优化,此解决方案效率不高,但符合功能哲学。效率低下的原因是每次迭代都会创建一个新字典。然而,我认为编译器能够通过避免实际复制来优化过程——尽管最好测量执行时间并与基于传统循环的解决方案的性能进行比较,以确定一个解决方案是否以及如何优于另一个解决方案。
您可以通过实施 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] = [=10=]
if let a = find(array, ) {
array[a].quantity += .quantity
} else {
array.append()
}
return array
}
我有一个对象数组,每个对象都有一个名称和一个数量。我想扫描整个数组,如果有任何对象具有相同的名称,则将所有已合并对象的总数减少到一个对象。
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 循环创建新数组来完成,但是是否有更多 'functional' 方法可以使用高阶函数(如 .filter、.reduce 甚至 .map)来完成?
你可以这样做:
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
可以使用reduce
方法将数组转换为字典,以名称为键,数组元素为值,populate/update相应:
// Pass an empty dictionary as initial value
anArray.reduce([String:AnObject]()) {
// The initial value parameter is immutable, so make a mutable copy
var dict = [=10=]
if dict[.name] != nil {
// If the key already exists, update the quantity
dict[.name]?.quantity += .quantity
} else {
// Otherwise add the element
dict[.name] =
}
return dict
}.values.array
最后一行获取 values
集合并将其转换为数组。
请注意,如果没有编译器优化,此解决方案效率不高,但符合功能哲学。效率低下的原因是每次迭代都会创建一个新字典。然而,我认为编译器能够通过避免实际复制来优化过程——尽管最好测量执行时间并与基于传统循环的解决方案的性能进行比较,以确定一个解决方案是否以及如何优于另一个解决方案。
您可以通过实施 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] = [=10=]
if let a = find(array, ) {
array[a].quantity += .quantity
} else {
array.append()
}
return array
}