0

I've recently started scripting in PowerShell and I'm having a difficulty in sorting a 2D array by multiple columns.

The following code is working fine. I have a 2D array (5x4) and I sort it by two columns; first, by the second column, and then by the first column.

$table1 = ("hhctrl.ocx",21,503,"Microsoft® HTML Help Control"),("mscomct2.ocx",10,629,"Microsoft Common Controls 2 ActiveX Control DLL"),("msscript.ocx",2,86,"Microsoft ® Script Control"),("sysmon.ocx",15,384,"System Monitor Control"),("tdc.ocx",1,61,"TDC ActiveX Control")
$table1_sorted = $table1 | Sort-Object @{Expression={$_[1]}; Ascending=$false}, @{Expression={$_[0]}; Ascending=$true}

echo (var_dump $table1)
echo (var_dump $table1_sorted)

var_dump is a custom function. I've created it to debug arrays. Write-Host and echo, they both flatten the arrays and don't seperate the items (no comma between items) e.g.

hhctrl.ocx 21 503 Microsoft® HTML Help Control mscomct2.ocx 10 629 Microsoft Common Controls 2 ActiveX Control DLL msscript.ocx 2 86 Microsoft ® Script Control sysmon.ocx 15 384 System Monitor Control tdc.ocx 1 61 TDC ActiveX Control

var_dump outputs:

[
    [
        "hhctrl.ocx",
        21,
        503,
        "Microsoft® HTML Help Control"
    ],
    [
        "mscomct2.ocx",
        10,
        629,
        "Microsoft Common Controls 2 ActiveX Control DLL"
    ],
    [
        "msscript.ocx",
        2,
        86,
        "Microsoft ® Script Control"
    ],
    [
        "sysmon.ocx",
        15,
        384,
        "System Monitor Control"
    ],
    [
        "tdc.ocx",
        1,
        61,
        "TDC ActiveX Control"
    ]
]
[
    [
        "hhctrl.ocx",
        21,
        503,
        "Microsoft® HTML Help Control"
    ],
    [
        "sysmon.ocx",
        15,
        384,
        "System Monitor Control"
    ],
    [
        "mscomct2.ocx",
        10,
        629,
        "Microsoft Common Controls 2 ActiveX Control DLL"
    ],
    [
        "msscript.ocx",
        2,
        86,
        "Microsoft ® Script Control"
    ],
    [
        "tdc.ocx",
        1,
        61,
        "TDC ActiveX Control"
    ]
]

Now, if I use another array, "table" with only one row, sorting flattens the array.

$table2 = ,("hhctrl.ocx",21,503,"Microsoft® HTML Help Control")
$table2_sorted = $table2 | Sort-Object @{Expression={$_[1]}; Ascending=$false}, @{Expression={$_[0]}; Ascending=$true}

echo (var_dump $table2)
echo (var_dump $table2_sorted)

Output:

[
    [
        "hhctrl.ocx",
        21,
        503,
        "Microsoft® HTML Help Control"
    ]
]
[
    "hhctrl.ocx",
    21,
    503,
    "Microsoft® HTML Help Control"
]

This happens when there's only one row. Why is that?

4
  • It's the pipeline that flattens your one-item array. Use Sort-Object -InputObject $table2 and it won't happen Commented Dec 4, 2016 at 13:14
  • @MathiasR.Jessen What you suggest doesn't seem to work. It just returns the $table2 (also $table1) unchanged. I've checked Sort-Object | SS64.com and How does -InputObject work?. There isn't any example that explicitly uses the -InputObject. Can you provide a working code? Commented Dec 4, 2016 at 17:22
  • 1
    $table2_sorted = @( $table2 | Sort-Object ... ) Commented Dec 4, 2016 at 17:30
  • @PetSerAl Strangely, this worked. If I got it right, you're wrapping the returned value in an array. I thought it would also wrap the multi-row array $table1 in another array, thus it'd become a 3D array, but it didn't. It just wraps the one with one row $table2. Can I rely on this? Why doesn't it also wrap $table1 in an array? Commented Dec 4, 2016 at 17:44

1 Answer 1

1

Sort-Object does not return array. It return write to pipeline individual input items in sorted order.

PS> 7,3,8,5,1 | Sort-Object | ForEach-Object { Write-Host "Item: $_" }
Item: 1
Item: 3
Item: 5
Item: 7
Item: 8

As you can see, next command in pipeline ForEach-Object see each individual item, but not array as whole.

Wrapping to array in $table1 case happens, because last command in pipeline wrote more than one item in pipeline. In $table2 case last command in pipeline wrote only one item, so no wrapping necessary there. If you want to always wrap pipeline results into array regardless of amount of results (zero, one or many), then you need to use array subexpression operator:

$Results = @( First-Command | Middle-Command | Last-Command )
Sign up to request clarification or add additional context in comments.

1 Comment

So, multiple items written to "pipeline" is treated as an array, thus $table1_sorted is an array of arrays. $table2_sorted isn't 2D, because Sort-Object "returned" only one item and one item alone doesn't treated as an array. And if I wrap the sort output in an array $sorted = @($table | Sort-Object ... ), I always get an array containing the items, no matter what the item count is. It makes sense now.

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.