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?

akinuri
  • 10,690
  • 10
  • 65
  • 102
  • It's the pipeline that flattens your one-item array. Use `Sort-Object -InputObject $table2` and it won't happen – Mathias R. Jessen Dec 04 '16 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](http://ss64.com/ps/sort-object.html) and [How does -InputObject work?](http://stackoverflow.com/a/35292369/2202732). There isn't any example that explicitly uses the `-InputObject`. Can you provide a working code? – akinuri Dec 04 '16 at 17:22
  • 1
    `$table2_sorted = @( $table2 | Sort-Object ... )` – user4003407 Dec 04 '16 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? – akinuri Dec 04 '16 at 17:44

1 Answers1

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 )
user4003407
  • 21,204
  • 4
  • 50
  • 60
  • 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. – akinuri Dec 04 '16 at 20:58