Sort a PowerShell hash table on a property of the value

21,043

Solution 1

Sort-Object accepts a property name or a script block used to sort. Since you're trying to sort on a property of a property, you'll need to use a script block:

Write-Host "Ascending"
$h.GetEnumerator() | 
    Sort-Object { $_.Value.SortOrder } | 
    ForEach-Object {  Write-Host $_.Value.SortOrder }

Write-Host "Descending"
$h.GetEnumerator() |
    Sort-Object { $_.Value.SortOrder } -Descending |
    ForEach-Object { Write-Host $_.Value.SortOrder }

You can filter using the Where-Object cmdlet:

Write-Host "Ascending"
$h.GetEnumerator() | 
    Where-Object { $_.Name -ge 2 } |
    Sort-Object { $_.Value.SortOrder } | 
    ForEach-Object {  Write-Host $_.Value.SortOrder }

You usually want to put Where-Object before any Sort-Object cmdlets, since it makes sorting faster.

Solution 2

I was using a hash table as a frequency table, to count the occurrence of words in filenames.

$words = @{}
get-childitem *.pdf | foreach-object -process {
    $name = $_.name.substring($_.name.indexof("-") + 1, $_.name.indexof(".") - $_.name.indexof("-") - 1)
    $name = $name.replace("_", " ")
    $word = $name.split(" ")[0]
    if ( $words.contains($word) ){
        $words[$word] = $words[$word] + 1 
    }else{
        $words.add($word, 1)
    }
}
$words.getenumerator() | sort-object -property value

It's that last line that does the magic, sorting the hash table on the value(frequency).

Share:
21,043
Dan P
Author by

Dan P

Updated on February 05, 2020

Comments

  • Dan P
    Dan P over 4 years

    I'm having an issue sorting a hash table. I've broken down my code to just bare necessities so as not to overwhelm anyone with my original script.

    Write-Host "PowerShell Version = " ([string]$psversiontable.psversion) 
    $h = @{}
    $Value = @{SortOrder=1;v1=1;}
    $h.Add(1, $Value)
    $Value = @{SortOrder=2;v1=1;}
    $h.Add(2, $Value)
    $Value = @{SortOrder=3;v1=1;}
    $h.Add(3, $Value)
    $Value = @{SortOrder=4;v1=1;}
    $h.Add(4, $Value)
    
    Write-Host "Ascending"
    foreach($f in $h.GetEnumerator() | Sort-Object Value.SortOrder)
    {
        Write-Host $f.Value.SortOrder
    }
    
    Write-Host "Descending"
    foreach($f in $h.GetEnumerator() | Sort-Object Value.SortOrder -descending)
    {
        Write-Host $f.Value.SortOrder
    }
    

    The output is

    PowerShell Version =  3.0
    Ascending
    2
    1
    4
    3
    Descending
    2
    1
    4
    3
    

    I'm sure this is just a simple case of not knowing the correct usage of Sort-Object. The sort works correctly on Sort-Object Name so maybe it has something to do with not knowing how to handle the Value.SortOrder?

  • Dan P
    Dan P almost 11 years
    I've updated my question to use the code you mentioned but I still get the same output.
  • Dan P
    Dan P almost 11 years
    nevermind looks like I missed the period. I'll re edit the question back to the original.
  • Dan P
    Dan P almost 11 years
    btw this may be separate question but is there easy way to >= 2 or some other filter.
  • Ates Goral
    Ates Goral almost 11 years
    Yes, in very similar fashion with Where-Object. Add "| Where-Object { $_.Value.SortOrder -ge 2 }" before your | Sort-Object, without the quotes.
  • Thomas Maierhofer
    Thomas Maierhofer almost 10 years
    No need to use a script block. $h.GetEnumerator() | sort Name will do
  • Aaron Jensen
    Aaron Jensen almost 10 years
    @ThomasMaierhofer No. The user doesn't want to sort by a Name property. He wants to sort by a property on a property of the object being passed.
  • Thomas Maierhofer
    Thomas Maierhofer almost 10 years
    You're right. Seems that I haven't recognized the whole problem
  • Alan
    Alan over 6 years
    That's a brilliant answer. I've been poking around for a couple of hours and seen so many complicated answers, but this works perfect. Well done! I needed the sort in a hash table, but it was a simple matter to put $hashtable = {what you wrote} and it worked great. Thanks so much.