3

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?

1
  • 1
    A simple way to do it would be to use 2 expressions: ((101,43),(101,42),(99,42),(100,42)) | Sort-Object { $_[0] }, { $_[1] } | ForEach-Object { "[$_]" }. A Tuple would also work ((101,43),(101,42),(99,42),(100,42)) | Sort-Object { [Tuple]::Create($_[0], $_[1]) } | ForEach-Object { "[$_]" } Commented Oct 25, 2022 at 20:59

1 Answer 1

3
  • 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]

# -> '<100 42>', '<99 42>' - note the -Descending
(99, 42), (100, 42) | 
  Sort-Object -Descending { $_.ForEach({ '{0,11}' -f $_ }) } | 
  ForEach-Object { "<$_>" }

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.IComparable interface, such as tuples (System.Tuple) and value tuples (System.ValueTuple), are compared via that interface by Sort-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:

# Works for arrays of up to 8 elements.
# -> '<1 2 3 4 5 6 7 10>', '<1 2 3 4 5 6 7 9>'
(1..7 + 9), (1..7 + 10) | 
  Sort-Object -Descending { [ValueTuple]::Create.Invoke($_) } | 
  ForEach-Object { "<$_>" }

Potential future improvement:

As of PowerShell 7.2.x, only input objects that implement the System.IComparable interface are compared via that interface, (unfortunately) not also collection objects that (only) implement the System.Collections.IStructuralComparable interface, such as .NET arrays (although many other array-like collection types do not).

GitHub issue #18389 requests that Sort-Object honor input objects whose types implement (only) System.Collections.IStructuralComparable too.


[1] Strictly speaking, a PowerShell array literal such as 99, 42 is 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 yields 11. See also: -f, the format operator

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.