Ios 是否可以使用高阶函数合并阵列中的重复对象?
我有一个对象数组,每个对象都有一个名称和一个数量。我想扫描整个数组,如果有任何对象具有相同的名称,将合并的所有对象的总数减少为一个对象Ios 是否可以使用高阶函数合并阵列中的重复对象?,ios,swift,functional-programming,Ios,Swift,Functional Programming,我有一个对象数组,每个对象都有一个名称和一个数量。我想扫描整个数组,如果有任何对象具有相同的名称,将合并的所有对象的总数减少为一个对象 struct AnObject { var name: String var quantity: UInt } var anArray = [AnObject(name: "one", quantity: 2), AnObject(name: "two", quantity: 2),
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?您可以这样做:
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
您可以这样做:
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
方法将数组转换为字典,名称作为键,数组元素作为值,并相应地填充/更新:
// 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
最后一行获取值
集合并将其转换为数组
要注意的是,如果没有编译器优化,这个解决方案不是有效的,而是遵循函数原理。效率低下的原因是每次迭代都会创建一个新字典。然而,我认为编译器能够通过避免实际副本来优化流程——尽管最好测量执行时间并与传统的基于循环的解决方案的性能进行比较,以确定一个解决方案是否以及如何优于另一个解决方案。您可以使用
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
最后一行获取值
集合并将其转换为数组
要注意的是,如果没有编译器优化,这个解决方案不是有效的,而是遵循函数原理。效率低下的原因是每次迭代都会创建一个新字典。然而,我认为编译器能够通过避免实际副本来优化流程——尽管最好测量执行时间并与传统的基于循环的解决方案的性能进行比较,以确定一个解决方案是否以及如何优于另一个解决方案。您可以通过实现
equalable
:
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
}
通过实现
equalable
,可以使用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
}
这是一个很好的答案,但不是针对我问的问题:-)您可以创建一个扩展,它的行为类似于
.filter
。但在你的内心,你需要这些环。@DanielCreagh,为什么?考虑到您的anObject
类型和anArray
数据集,@ThomasKilian的解决方案给出了与@Antonio完全相同的结果:这是一个很好的答案,但不是针对我问的问题:-)您可以创建一个扩展,其行为类似于。filter
。但在你的内心,你需要这些环。@DanielCreagh,为什么?鉴于您的anObject
类型和anArray
数据集,@ThomasKilian的解决方案给出了与@Antonio完全相同的结果: