PowerShell:在哈希表值集合中排序

PowerShell: Sorting Within Hashtable Value Collections

我有一个测试输入的csv文件,如下:

ID;Product;Price;Discount;Level
1;Alpha;23.00;0.03;A
2;Bravo;17.00;0.01;A
3;Charlie;11.00;0.05;A
4;Delta;17.00;0.05;A
5;Echo;29.00;0.07;A
6;Foxtrot;11.00;0.01;A
7;Golf;11.00;0.01;A
1;Hotel;53.00;0.11;B
2;India;53.00;0.13;B
3;Juliet;61.00;0.11;B
1;Kilo;79.00;0.23;C
2;Lima;89.00;0.23;C
3;Mike;97.00;0.29;C
4;November;83.00;0.17;C
5;Oscar;79.00;0.11;C

我想生成以下输出文件:

ID;Product;Price;Discount;Level
1;Alpha;23.00;0.03;A
5;Echo;29.00;0.07;A
2;India;53.00;0.13;B
3;Juliet;61.00;0.11;B
2;Lima;89.00;0.23;C
3;Mike;97.00;0.29;C

也就是说,对于每个级别,我想 select 前两行按价格排序,然后按折扣排序。例如,对于级别 B,我想要 JulietIndia 而不是 JulietHotel

我有以下代码片段,但没有完全实现!

$input = '.\TestInput.csv'
$products = @(Import-CSV -Path $input -Delimiter ";")
$levels = $products | 
    Group-Object -Property Level -AsHashTable 

$sales = $levels.GetEnumerator() | 
    Sort-Object -Property @{ Expression = { [int]($_.Price) } ; Descending = $true },
                          @{ Expression = { [int]($_.Discount) } ; Descending = $true } | 
    Select-Object -first 2

$output = '.\TestOutput.csv'
$sales | Export-Csv -Path $output -Delimiter ";" -NoTypeInformation

我错过了什么?

$sales = ForEach ($Level in $levels.Keys | Sort-Object) { $levels.$Level | Sort-Object -Property  price,discount | Select-Object -last 2 }

直接在Import-Csv输出上使用Group-Object

Import-Csv '.\TestInput.csv' -Delimiter ';' | 
  Group-Object Level | 
    ForEach-Object { 
      $_.Group |
        Sort-Object { [int] $_.Price }, { [int] $_.Discount } |
          Select-Object -Last 2
    } | 
        Export-Csv -Path '.\TestOutput.csv' -Delimiter ";" -NoTypeInformation

注意:在 PowerShell [Core] v6+ 中,您可以将 | Select-Object Last 2 替换为 -Bottom 2,前提是 Sort-Object 现在支持 -Top-Bottom 参数.


至于你试过的

  • 虽然 Group-Object 通常按指定的分组标准(在本例中为 Level)对结果组进行排序,但如果您使用-AsHashtable,鉴于哈希表条目本质上是无序的。

    • 为防止出现这种情况,请使用默认输出 (no -AsHashtable) - 生成单独的组对象 - 如上所示,或者添加最终的 Sort-Object 调用按 Level.
    • 排序
  • $levels.GetEnumerator() 通过管道发送键值 System.Collections.DictionaryEntry 个实例),其 .Key 属性 是分组标准 (.Level),其 .Value 属性 是关联组。

    • 但是,您应该只通过管道发送条目 values - 即组对象,而不是键值对;这可以通过简单地访问哈希表的 .Values 属性 来实现
      • 但是,由于您需要单独处理每个组,要在每个中找到最大值,您需要一个中间ForEach-Object 在其中调用以执行特定于组的处理。
  • 您正在使用 - 基于哈希表的动态 属性 定义 - 指定 Sort-Object 的条件;然而,对于 Sort-Object 来说,这是永远没有必要的,因为你永远不会看到这样一个 属性 的 name;因此,直接使用上面的表达式脚本块(如图)就足够了。

  • 由于您的排序是 降序Select-Object -First 2 将显示两个最高值,但是 降序 顺序,而您想要的输出要求它们按 升序排列

    • 要得到后者,排序升序,然后select 最后 2 个对象。

将它们放在一起(但请注意,顶部的解决方案在概念上更简单且更有效):

$sales = 
  $levels.Values | ForEach-Object { 
      $_ |  # process the group at hand
        Sort-Object -Property { [int] $_.Price }, { [int] $_.Discount } | 
          Select-Object -Last 2
    } | Sort-Object Level