将对象列表中的值收集到 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(也许更快)的方法来完成此操作
谢谢!
通过每月对数组进行分组创建字典,然后使用 mapValues
和 reduce
计算每月总和
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))
}
}
我有一个包含多个属性的 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(也许更快)的方法来完成此操作谢谢!
通过每月对数组进行分组创建字典,然后使用 mapValues
和 reduce
计算每月总和
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))
}
}