在 Powershell 中通过 LinQ 对数据进行分组
Grouping data via LinQ in Powershell
我有一个数据数组,我需要将它们按 2 个属性分组,然后计算每组第三个属性的总和。我想通过 Linq 尽可能快地完成此操作。
到目前为止,这是我的演示代码:
class costs {
[string] $first;
[string] $last;
[int] $price;
costs([string]$first, [string]$last, [int] $price){
$this.first = $first
$this.last = $last
$this.price = $price
}
}
[costs[]]$costs = @(
[costs]::new('peter', 'parker', 1),
[costs]::new('peter', 'parker', 2),
[costs]::new('paul', 'summer', 3),
[costs]::new('paul', 'winter', 4),
[costs]::new('mary', 'winter', 5)
)
# group by full name:
$groupBy = [Func[Object,string]] {$args[0].first + $args[0].last}
$groupResult = [Linq.Enumerable]::GroupBy($costs, $groupBy)
# sum the costs per group:
$selectFunc = [Func[Object,int]] {$sum=0; foreach($p in $args[0].price){$sum += $p};$sum}
$selectResult = [Linq.Enumerable]::Select($groupResult, $selectFunc)
$selectResult
selectResult 向我显示了每个用户的正确费用总和。
但我正在努力从初始数组中获得两个用户属性的总和。
我也不确定,如果我可以将两个 Linq 调用组合在一个调用中以使其更快。
在这里欢迎任何输入(除了“为什么使用 Linq?”)。
更新
根据答案,我更新了代码:
class costs {
[string] $first;
[string] $last;
[int] $price;
costs([string]$first, [string]$last, [int] $price){
$this.first = $first
$this.last = $last
$this.price = $price
}
}
[costs[]]$costs = @(
[costs]::new('peter', 'parker', 1),
[costs]::new('peter', 'parker', 2),
[costs]::new('paul', 'summer', 3),
[costs]::new('paul', 'winter', 4),
[costs]::new('mary', 'winter', 5)
)
foreach($doubler in 0..15){$costs += $costs}
cls
write-host "processing $($costs.count) elements."
(measure-command {
# group by full name:
$groupBy = [Func[Object,string]] {$args[0].first + $args[0].last}
$groupResult = [Linq.Enumerable]::GroupBy($costs, $groupBy)
# sum the costs per group:
$selectFunc = [Func[Object,Object]]{
$sum=0
foreach($p in $args[0].price){
$sum += $p
}
foreach($a in $args[0]) {
[costs]::new($a.first, $a.last, $sum)
break
}
}
$selectResult = [Linq.Enumerable]::Select($groupResult, $selectFunc)
$result = [Linq.Enumerable]::ToArray($selectResult)
}).TotalSeconds
$result
# and for the books the same procedure with a dataTable (slower):
$table = [System.Data.DataTable]::new('table')
[void]$table.Columns.Add('first', [string])
[void]$table.Columns.Add('last', [string])
[void]$table.Columns.Add('price', [int])
$resultTable = $table.Clone()
# fill table with above test-data:
foreach($c in $costs){
$null = $table.rows.Add($c.first, $c.last, $c.price)
}
(measure-command {
$groupBy = [Func[System.Data.DataRow,string]] {$args[0].first + $args[0].last}
$groupResult = [Linq.Enumerable]::GroupBy([System.Data.DataRow[]]$table.Rows, $groupBy)
# sum the costs per group:
$selectFunc = [Func[object,System.Data.DataRow]]{
$sum=0
foreach($p in $args[0].price){
$sum += $p
}
foreach($a in $args[0]) {
$resultTable.rows.Add($a.first, $a.last, $sum)
break
}
}
$selectResult = [Linq.Enumerable]::Select($groupResult, $selectFunc)
$null = [Linq.Enumerable]::ToList($selectResult)
}).TotalSeconds
$resultTable
超过 300000 个元素的运行时间约为 2.5 秒。没有那么糟糕。直到现在,如果不切换到嵌入式 C# 代码,我找不到更快的方法。
将 $selectFunc
定义更改为 return [psobject]
或 [object]
,然后从现有分组值创建结果对象:
$selectFunc = [Func[Object,psobject]]{
$sum=0
foreach($p in $args[0].price){
$sum += $p
}
# Output new object with first+last based on input object + sum
$args[0] |Select first,last,@{Name='sum';Expression={$sum}} -First 1
}
I want to do this via Linq to be as fast as possible.
我强烈建议您实际 测试 这是否比使用 Group-Object
或用于计算的简单哈希表更快 - 很多开销这会使 PowerShell 变慢(尤其是参数绑定),但仍将适用于您的代码,因此差异可能并不显着 - 但脚本的 可读性 可能会受到很大影响。
我个人的偏好是只使用 Group-Object
cmdlet:
$costs |Group-Object first,last |ForEach-Object {
$sum = ($_.Group |Measure price -Sum).Sum
$_.Group |Select -Property first,last,@{N='Sum';E={$sum}} -First 1
}
我有一个数据数组,我需要将它们按 2 个属性分组,然后计算每组第三个属性的总和。我想通过 Linq 尽可能快地完成此操作。
到目前为止,这是我的演示代码:
class costs {
[string] $first;
[string] $last;
[int] $price;
costs([string]$first, [string]$last, [int] $price){
$this.first = $first
$this.last = $last
$this.price = $price
}
}
[costs[]]$costs = @(
[costs]::new('peter', 'parker', 1),
[costs]::new('peter', 'parker', 2),
[costs]::new('paul', 'summer', 3),
[costs]::new('paul', 'winter', 4),
[costs]::new('mary', 'winter', 5)
)
# group by full name:
$groupBy = [Func[Object,string]] {$args[0].first + $args[0].last}
$groupResult = [Linq.Enumerable]::GroupBy($costs, $groupBy)
# sum the costs per group:
$selectFunc = [Func[Object,int]] {$sum=0; foreach($p in $args[0].price){$sum += $p};$sum}
$selectResult = [Linq.Enumerable]::Select($groupResult, $selectFunc)
$selectResult
selectResult 向我显示了每个用户的正确费用总和。 但我正在努力从初始数组中获得两个用户属性的总和。 我也不确定,如果我可以将两个 Linq 调用组合在一个调用中以使其更快。 在这里欢迎任何输入(除了“为什么使用 Linq?”)。
更新
根据答案,我更新了代码:
class costs {
[string] $first;
[string] $last;
[int] $price;
costs([string]$first, [string]$last, [int] $price){
$this.first = $first
$this.last = $last
$this.price = $price
}
}
[costs[]]$costs = @(
[costs]::new('peter', 'parker', 1),
[costs]::new('peter', 'parker', 2),
[costs]::new('paul', 'summer', 3),
[costs]::new('paul', 'winter', 4),
[costs]::new('mary', 'winter', 5)
)
foreach($doubler in 0..15){$costs += $costs}
cls
write-host "processing $($costs.count) elements."
(measure-command {
# group by full name:
$groupBy = [Func[Object,string]] {$args[0].first + $args[0].last}
$groupResult = [Linq.Enumerable]::GroupBy($costs, $groupBy)
# sum the costs per group:
$selectFunc = [Func[Object,Object]]{
$sum=0
foreach($p in $args[0].price){
$sum += $p
}
foreach($a in $args[0]) {
[costs]::new($a.first, $a.last, $sum)
break
}
}
$selectResult = [Linq.Enumerable]::Select($groupResult, $selectFunc)
$result = [Linq.Enumerable]::ToArray($selectResult)
}).TotalSeconds
$result
# and for the books the same procedure with a dataTable (slower):
$table = [System.Data.DataTable]::new('table')
[void]$table.Columns.Add('first', [string])
[void]$table.Columns.Add('last', [string])
[void]$table.Columns.Add('price', [int])
$resultTable = $table.Clone()
# fill table with above test-data:
foreach($c in $costs){
$null = $table.rows.Add($c.first, $c.last, $c.price)
}
(measure-command {
$groupBy = [Func[System.Data.DataRow,string]] {$args[0].first + $args[0].last}
$groupResult = [Linq.Enumerable]::GroupBy([System.Data.DataRow[]]$table.Rows, $groupBy)
# sum the costs per group:
$selectFunc = [Func[object,System.Data.DataRow]]{
$sum=0
foreach($p in $args[0].price){
$sum += $p
}
foreach($a in $args[0]) {
$resultTable.rows.Add($a.first, $a.last, $sum)
break
}
}
$selectResult = [Linq.Enumerable]::Select($groupResult, $selectFunc)
$null = [Linq.Enumerable]::ToList($selectResult)
}).TotalSeconds
$resultTable
超过 300000 个元素的运行时间约为 2.5 秒。没有那么糟糕。直到现在,如果不切换到嵌入式 C# 代码,我找不到更快的方法。
将 $selectFunc
定义更改为 return [psobject]
或 [object]
,然后从现有分组值创建结果对象:
$selectFunc = [Func[Object,psobject]]{
$sum=0
foreach($p in $args[0].price){
$sum += $p
}
# Output new object with first+last based on input object + sum
$args[0] |Select first,last,@{Name='sum';Expression={$sum}} -First 1
}
I want to do this via Linq to be as fast as possible.
我强烈建议您实际 测试 这是否比使用 Group-Object
或用于计算的简单哈希表更快 - 很多开销这会使 PowerShell 变慢(尤其是参数绑定),但仍将适用于您的代码,因此差异可能并不显着 - 但脚本的 可读性 可能会受到很大影响。
我个人的偏好是只使用 Group-Object
cmdlet:
$costs |Group-Object first,last |ForEach-Object {
$sum = ($_.Group |Measure price -Sum).Sum
$_.Group |Select -Property first,last,@{N='Sum';E={$sum}} -First 1
}