Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Sort-Object on array of hash tables

After creating the following array of hash tables in PowerShell (version 2):

$array = @()
$array += @{a = 1; b = 5}
$array += @{a = 3; b = 4}
$array += @{a = 5; b = 9}
$array += @{a = 7; b = 2}

All the following calls to Sort-Object return the same {a,b} sequence: {5,9},{7,2},{1,5},{3,4}:

$array | Sort-Object
$array | Sort-Object -Descending
$array | Sort-Object a
$array | Sort-Object b
$array | Sort-Object "a"
$array | Sort-Object -Property a
$array.GetEnumerator() | Sort-Object
$array.GetEnumerator() | Sort-Object a

If $array is called directly, it returns the objects in the order that they were entered, so the sort is changing the order. But perhaps it has no comparer so just treats every object as equal??

The goal is to sort all of the objects. I am fine with workarounds that involve an alternate custom object, an alternate collection and/or an alternate sort routine.

Edit: To clarify what I'm trying to achieve, this would be a rough equivalent in C#

class MyClass {
    public int a;
    public int b;
}

// somewhere inside a method:
var objects = new List<MyClass>();
// code here to populate objects with some MyClass instances.
var sorted = objects.OrderBy(x => x.a);
like image 981
Hutch Avatar asked May 04 '16 17:05

Hutch


2 Answers

Sort-Object a is not working because a isn't a property, it's a key in a hashtable. One might be confused because PowerShell hides this for you by accepting $ht.a even though it actually uses $ht["a"] behind the scenes, but if you run $array | Get-Member you can confirm that the property a doesn't exist.

A workaround is to sort by using calculated properties that access the keys in the hashtable. I used Select-Object to show the hashtables in a table so it's easier to see the results. Ex:

$array = @()
$array += @{a = 7; b = 2}
$array += @{a = 3; b = 4}
$array += @{a = 3; b = 1}
$array += @{a = 5; b = 9}
$array += @{a = 1; b = 5}

#No sort
$array | Select-Object @{n="a";e={$_.a}}, @{n="b";e={$_.b}}

a b
- -
7 2
3 4
3 1
5 9
1 5

#Sort by a asc, then b asc
$array | Sort-Object { $_.a },  { $_.b } | Select-Object @{n="a";e={$_.a}}, @{n="b";e={$_.b}}

a b
- -
1 5
3 1
3 4
5 9
7 2
like image 141
Frode F. Avatar answered Sep 27 '22 20:09

Frode F.


See answer by @Frode F. for a detailed explanation, but that answer does miss one situation. When the outer container only has one item (i.e. when the array only contains one hashtable), Sort-Object "conveniently" sorts the inner container.

So... Where $array.Count -eq 1, the elements of that hashtable are sorted. Where $array.Count -gt 1, the elements of the array are sorted.

As a workaround, use:

if ($array.Count -gt 1) {
    $result = Sort-Object { $_.a }, {$_.b }
} else {
    $result = $array
}    
like image 37
Hutch Avatar answered Sep 27 '22 22:09

Hutch