gvee's helpful answer is a pragmatic solution that ensures that the columns appear in the desired order, because the order in which you pass property names to Select-Object
is the order in which the properties are added to the resulting [pscustomobject]
instances.
It is, however, inefficient, because the desired column order can be ensured at the time $Record
is defined, without needing an additional pipeline stage that effectively duplicates the result objects:
Define $Record
as an ordered hashtable as follows (requires PSv3+; optional quoting omitted; use of single quotes):
$Record = [ordered] @{
'Group Name' = ''
Name = ''
Username = ''
}
This guarantees that the [pscustomobject]
instances later created by the New-Object PSObject -property $Record
calls contain properties in the same order as the keys were defined in $Record
.
Two asides:
New-Object PSObject -property $Record
could be simplified to [pscustomobject] $Record
(see below)
- Building up a large array incrementally is more efficiently handled with a
[System.Collections.ArrayList]
instance to which you add elements with .Add()
rather than using PowerShell's built-in arrays with +=
, which creates a copy of the array every time. Even better is to let PowerShell create the array for you, simply by capturing the output from your foreach
loop in a variable ($Table = foreach ...
- see this answer)
Supplemental information:
The source of the problem is that regular hashtables ([hashtable]
instances) enumerate their keys in an effectively unpredictable order (the order is an implementation detail and not guaranteed), and when you create a [pscustomobject]
from a hashtable, that unpredictable key ordering is reflected in the ordering of the resulting object's properties.
By contrast, in PSv3+ you can create an ordered hashtable by placing the [ordered]
keyword before a hashtable literal, which results in a [System.Collections.Specialized.OrderedDictionary]
instance whose keys are ordered based on the order in which they were added.
Creating a [pscustomobject]
instance from an ordered hashtable then preserves that key ordering in the resulting object's properties.
Note that PowerShell v3+ offers a convenient shortcut for creating a [pscustomobject]
instance from a hashtable using a cast; e.g.:
PS> [pscustomobject] @{ a = 1; b = 2; c = 3 } # key order is PRESERVED
a b c
- - -
1 2 3
Note how the key-definition order was preserved, even though [ordered]
was not specified.
In other words: When you cast a hashtable literal directly to [pscustomobject]
, [ordered]
is implied, so the above is equivalent to:
[pscustomobject] [ordered] @{ a = 1; b = 2; c = 3 } # [ordered] is optional
Caveat: This implicit ordering only applies when a hashtable literal is directly cast to [pscustomboject]
, so the key/property order is not preserved in the following variations:
New-Object PSCustomObject -Property @{ a = 1; b = 2; c = 3 } # !! order NOT preserved
$ht = @{ a = 1; b = 2; c = 3 }; [pscustomobject] $ht # !! order NOT preserved
[pscustomobject] (@{ a = 1; b = 2; c = 3 }) # !! order NOT preserved, due to (...)
Therefore, when not casting a hashtable literal directly to [pscustomobject]
, define it with [ordered]
explicitly.