将对象列表中的值收集到 swift5 中的字典中

Collect values from list of objects into dictionary in swift5

我有一个包含多个属性的 Employee 对象列表(一个 Employee 对象数组),我想获得一个包含月份和工作时间。我查看了其他帖子,但我只找到了可以使用 map -> .map(\.month) 而不是两个值

收集其中一项的示例
    struct Employee {
      var id: UUID
      var name: String
      var month: Date
      var hoursWorked: Double
      var ...
      var ...
    }

示例数据 - Employee 对象数组:

{month: 2019-01, hoursWorked: 256, id:..., name...} 
{month: 2019-02, hoursWorked: 200, id:..., name...}
{month: 2019-03, hoursWorked: 300, id:..., name...}
{month: 2019-04, hoursWorked: 150, id:..., name...}
{month: 2019-05, hoursWorked: 100, id:..., name...}
{month: 2019-06, hoursWorked: 220, id:..., name...}
.
.
.

预期结果: 由月份和工作小时数组成的字典:

[2019-01: 256.0]
[2019-02: 200.0]
[2019-03: 300.0]
[2019-04: 150.0]
[2019-05: 100.0]
[2019-06: 220.0]

...

实现此目的的一种方法是遍历 Employee 数组并将月份和工作小时数收集到 Swift 字典中(Swift 中的日期:双精度)(类似于 Swift 中的哈希映射=27=]).我想知道在 Swift.

中是否有 better/simpler(也许更快)的方法来完成此操作

谢谢!

通过每月对数组进行分组创建字典,然后使用 mapValuesreduce 计算每月总和

let stats = Dictionary(grouping: employees, by: \.month)
    .mapValues { [=10=].reduce(into: 0, { [=10=] += .hoursWorked }) }

编辑:这是一个基于@Alexander-ReinstateMonica

评论的替代解决方案
extension Sequence {
    func sum() -> Element where Element: AdditiveArithmetic {
        self.reduce(.zero, +)
    }
}

let stats = Dictionary(grouping: employees, by: \.month)
    .mapValues { [=11=].lazy.map(\.hoursWorked).sum() }

编辑 2:元组而不是字典

上面的两种解决方案都非常容易转换为元组数组,这里是第二种解决方案,因为元组按月排序

let stats = Dictionary(grouping: employees, by: \.month)
    .map( { ([=12=].key, [=12=].value.lazy.map(\.hoursWorked).sum()) })
    .sorted(by: {[=12=].0 < .0})

您可以为此使用 reduce(into:_:) 函数:

let dictionary = emplyees.reduce(into: [Date: Double]()) { (result, employee) in
    result[employee.month, default: 0] += employee.hoursWorked
}

这将创建一个字典 [Date: Double],其中以月份作为键,以该月的总工作小时数作为值。

更新:为了return一个元组数组,你需要一些额外的工作:

let dictionary = emplyees.reduce(into: [(month: Date, hours: Double)]()) { (result, employee) in
    if let index = result.firstIndex(where: { [=11=].month == employee.month }) {
        result[index].hours += employee.hoursWorked
    } else {
        result.append((month: employee.month, hours: employee.hoursWorked))
    }
}