是否可以使用高阶函数来合并数组中的重复对象?

时间:2015-03-03 12:47:46

标签: ios swift functional-programming

我有一个对象数组,每个对象都有一个名称和数量。我想扫描数组,如果有任何对象具有相同的名称,则将所有那些已合并的对象的总数减少到一个单个对象。

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等高阶函数是否有更“实用”的方法呢?

3 个答案:

答案 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
}