I would like to sort an array of arrays of integers. And yes, I expect the sort function to sort first based on the first integer, then the second, and so on.
So for this array of array of ints:
((99,42),(100,42))
I would expect the first element (99,42) to be before the second element (100,42)
Here is the naive solution:
$array_of_array | Sort-Object {$_}| foreach {Write-Output ("<"+$_+">")}
Much to my surprise this does not work:
((99,42),(100,42))| Sort-Object {$_}|foreach {Write-Output ("<"+$_+">")}
<100 42>
<99 42>
I debugged this and it seems I get a string comparison rather than an int comparison of each element. "100"<"99", but 100 > 99.
Here is a hack:
((99,42),(100,42))| Sort-Object {$_|foreach {($_+65)}}|foreach {Write-Output ("<"+$_+">")}
<99 42>
<100 42>
But why does this go wrong and how would I do this the right way?
Unfortunately, as of PowerShell 7.2.x, arrays do not sort meaningfully as-is, unless they happen to be composed of strings only.
The reason is that
[array]instances are in effect compared by their PowerShell-specific stringification, which is the space-concatenated list of their stringified elements.Workaround for
[int[]]arrays of arbitrary length:[1]The above converts each number in each input array to a left-space-padded string of fixed length (based on the max. length of an
[int]value expressed as a string[2]), which in effect converts each input array to a string array whose lexical sorting then equals how numerical sorting would work based on the original array's elements, based on PowerShell's array stringification.Workaround for arrays of up to 8 elements:
Array-like types that (also) implement the
System.IComparableinterface, such as tuples (System.Tuple) and value tuples (System.ValueTuple), are compared via that interface bySort-Object. This is what the workaround that Santiago Squarzon suggests in his comment on the question relies on; while there is a way not to have to enumerate the array elements one by one, the workaround is limited to arrays with at most 8 elements, based on the constructor overloads available:Potential future improvement:
As of PowerShell 7.2.x, only input objects that implement the
System.IComparableinterface are compared via that interface, (unfortunately) not also collection objects that (only) implement theSystem.Collections.IStructuralComparableinterface, such as .NET arrays (although many other array-like collection types do not).GitHub issue #18389 requests that
Sort-Objecthonor input objects whose types implement (only)System.Collections.IStructuralComparabletoo.[1] Strictly speaking, a PowerShell array literal such as
99, 42is an[object[]]array whose elements happen to be[int]instances. Similarly, the collection type that the intrinsic.ForEach()method emits is an array-like collection of type[System.Collections.ObjectModel.Collection[psobject]], but in terms of PowerShell stringification it behaves the same as an array.[2] Verify with
"$([int]::MinValue)".Length, which yields11. See also:-f, the format operator