如何在SWIFT 5中合并具有相同属性的数组元素,并为这些元素添加属性



我需要合并数组中具有相同属性的元素,并为这些元素添加一个属性。

的例子:


let manipulations: [Manipulation] = [
.init(productName: "Beer", responsibleName: "John", grossWeight: 100.00, cleanWeight: 30.00),
.init(productName: "Beer", responsibleName: "John", grossWeight: 200.00, cleanWeight: 50.00),
.init(productName: "Food", responsibleName: "Michael", grossWeight: 100.00, cleanWeight: 20.00),
.init(productName: "Food", responsibleName: "Michael", grossWeight: 400.00, cleanWeight: 100.00),
.init(productName: "Object", responsibleName: "John", grossWeight: 400.00, cleanWeight: 100.00),
.init(productName: "Beer", responsibleName: "John", grossWeight: 200.00, cleanWeight: 50.00),
.init(productName: "Beer", responsibleName: "John", grossWeight: 200.00, cleanWeight: 50.00),
.init(productName: "Beer", responsibleName: "John", grossWeight: 200.00, cleanWeight: 50.00),
.init(productName: "Water", responsibleName: "John", grossWeight: 200.00, cleanWeight: 50.00)
]
// MARK: - What i need:
print(manipulations)
// [
// Manipulation.(productName: "Beer", responsibleName: "John", grossWeight: 900.00,
// cleanWeight: 230.00),
// Manipulation.(productName: "Food", responsibleName: "Michael", grossWeight: 500.00,
// cleanWeight: 120.00),
// Manipulation.(productName: "Object", responsibleName: "John", grossWeight: 400.00,
// cleanWeight: 100.00)
// ]

如果你创建一个字典,你实际上可以将所有你想要求和的元素按键分组:

struct ManipulationKey {
let productName: String
let responsibleName: String
}
extension ManipulationKey: Hashable {}

let groupedElements = Dictionary(grouping: manipulations) { element in
return ManipulationKey(productName: element.productName, responsibleName: element.responsibleName)
}

当你把所有的东西分组时,你可以简单地循环遍历键/值并求和权重:

let mergedManipulations = groupedElements.keys.map {
var grossWeight = 0.0
var cleanWeight = 0.0
for manipulation in groupedElements[$0] ?? [] {
grossWeight += manipulation.grossWeight
cleanWeight += manipulation.cleanWeight
}
let newManipulation = Manipulation(
productName: $0.productName,
responsibleName: $0.responsibleName,
grossWeight: grossWeight,
cleanWeight: cleanWeight
)
return newManipulation
}

如果集合总是排序的,并且元素的顺序很重要,您可以使用reduce方法来获取结果的最后一个元素并将其与当前元素进行比较,如果它们相等,则求和,否则添加一个新元素。比如:

extension BidirectionalCollection where Element == Manipulation, Index == Int {
var summed: [Element] {
return reduce(into: []) {
if let lastElement = $0.last,
let lastIndex = $0.indices.last,
lastElement.productName == $1.productName,
lastElement.responsibleName == $1.responsibleName {
$0[lastIndex] = .init(
productName: lastElement.productName,
responsibleName: lastElement.responsibleName,
grossWeight: lastElement.grossWeight + $1.grossWeight,
cleanWeight: lastElement.cleanWeight + $1.cleanWeight
)
} else {
$0.append($1)
}
}
}
}

用法:

let summed = manipulations.summed
print(summed)

打印

[
操作(productName: "Beer" responblename: "John" grossWeight: 300.0, cleanWeight: 80.0),
操作(productName: "Food" responblename: "Michael" grossWeight: 500.0, cleanWeight: 120.0),
操作(productName: "Object" responblename: "John" grossWeight: 400.0, cleanWeight: 100.0)
]


编辑/更新:

如果元素没有排序,则需要在元素相加之前对它们进行排序:

let manipulations = [
("Beer"  , "John",    100,  30),
("Beer"  , "John",    200,  50),
("Food"  , "Michael", 100,  20),
("Food"  , "Michael", 400, 100),
("Object", "John",    400, 100),
("Beer"  , "John",    200,  50),
("Beer"  , "John",    200,  50),
("Beer"  , "John",    200,  50),
("Water" , "John",    200,  50)
].map(Manipulation.init)

let summed = manipulations.sorted {
($0.productName, $0.responsibleName) < ($1.productName, $1.responsibleName)
}.summed
print(summed)

这会打印:

[
操作(productName: "Beer" responblename: "John", grossWeight: 900.0, cleanWeight: 230.0),
操作(productName: "Food", responblename: "Michael", grossWeight: 500.0, cleanWeight: 120.0),
操作(productName: "Object", responblename: "John", grossWeight: 400.0, cleanWeight: 100.0),
操作(productName: "Water", responblename: "John", grossWeight: 100.0),
操作(productName: "Water", responblename: "John", grossWeight: 100.0):200.0, clean重量:50.0)
]


另一个建议是使你的结构符合哈希,并使用字典来跟踪元素的索引:

struct Manipulation {
let productName: String
let responsibleName: String
var grossWeight: Double = 0
var cleanWeight: Double = 0
}
extension Manipulation: Hashable {
static func == (lhs: Self, rhs: Self) -> Bool {
(lhs.productName, lhs.responsibleName) == (rhs.productName, rhs.responsibleName)
}
func hash(into hasher: inout Hasher) {
hasher.combine(productName)
hasher.combine(responsibleName)
}
}

你的computed属性看起来像:

extension BidirectionalCollection where Element == Manipulation, Index == Int {
var summed: [Element] {
var dict: [Element: Int] = [:]
return reduce(into: []) {
if let index = dict[$1] {
$0[index].grossWeight += $1.grossWeight
$0[index].cleanWeight += $1.cleanWeight
} else {
dict[$1] = $0.count
$0.append($1)
}
}
}
}

最新更新