Difference between PSObject, Hashtable, and PSCustomObject
Can anybody explain the details? If I create an object using
$var = [PSObject]@{a=1;b=2;c=3}
and then I look for its type using getType()
PowerShell tells me it's of type Hashtable.
When using Get-Member (alias gm
) to inspect the object it's obvious that a hashtable has been created, since it has a keys
and a values
property. So what's the difference to a "normal" hashtable?
Also, what's the advantage of using a PSCustomObject? When creating one using something like this
$var = [PSCustomObject]@{a=1;b=2;c=3}
the only visible difference to me is the different datatype of PSCustomObject. Also instead of keys and value properties, a inspection with gm
shows that now every key has been added as a NoteProperty object.
But what advantages do I have? I'm able to access my values by using its keys, just like in the hashtable. I can store more than simple key-value pairs (key-object pairs for example) in the PSCustomObject, JUST as in the hashtable. So what's the advantage? Are there any important differences?
One scenario where [PSCustomObject]
is used instead of HashTable
is when you need a collection of them. The following is to illustrate the difference in how they are handled:
$Hash = 1..10 | %{ @{Name="Object $_" ; Index=$_ ; Squared = $_*$_} }
$Custom = 1..10 | %{[PSCustomObject] @{Name="Object $_" ; Index=$_ ; Squared = $_*$_} }
$Hash | Format-Table -AutoSize
$Custom | Format-Table -AutoSize
$Hash | Export-Csv .\Hash.csv -NoTypeInformation
$Custom | Export-Csv .\CustomObject.csv -NoTypeInformation
Format-Table
will result in the following for $Hash
:
Name Value
---- -----
Name Object 1
Squared 1
Index 1
Name Object 2
Squared 4
Index 2
Name Object 3
Squared 9
...
And the following for $CustomObject
:
Name Index Squared
---- ----- -------
Object 1 1 1
Object 2 2 4
Object 3 3 9
Object 4 4 16
Object 5 5 25
...
The same thing happens with Export-Csv
, thus the reason to use [PSCustomObject]
instead of just plain HashTable
.
Say I want to create a folder. If I use a PSObject you can tell it is wrong by looking at it
PS > [PSObject] @{Path='foo'; Type='directory'}
Name Value
---- -----
Path foo
Type directory
However the PSCustomObject looks correct
PS > [PSCustomObject] @{Path='foo'; Type='directory'}
Path Type
---- ----
foo directory
I can then pipe the object
[PSCustomObject] @{Path='foo'; Type='directory'} | New-Item
From the PSObject
documentation:
Wraps an object providing alternate views of the available members and ways to extend them. Members can be methods, properties, parameterized properties, etc.
In other words, a PSObject
is an object that you can add methods and properties to after you've created it.
From the "About Hash Tables" documentation:
A hash table, also known as a dictionary or associative array, is a compact data structure that stores one or more key/value pairs.
...
Hash tables are frequently used because they are very efficient for finding and retrieving data.
You can use a PSObject
like a Hashtable
because PowerShell allows you to add properties to PSObjects
, but you shouldn't do this because you'll lose access to Hashtable
specific functionality, such as the Keys
and Values
properties. Also, there may be performance costs and additional memory usage.
The PowerShell documentation has the following information about PSCustomObject
:
Serves as a placeholder BaseObject when PSObject's constructor with no parameters is used.
This was unclear to me, but a post on a PowerShell forum from the co-author of a number of PowerShell books seems more clear:
[PSCustomObject] is a type accelerator. It constructs a PSObject, but does so in a way that results in hash table keys becoming properties. PSCustomObject isn't an object type per se – it's a process shortcut. ... PSCustomObject is a placeholder that's used when PSObject is called with no constructor parameters.
Regarding your code, @{a=1;b=2;c=3}
is a Hashtable
. [PSObject]@{a=1;b=2;c=3}
doesn't convert the Hashtable
to a PSObject
or generate an error. The object remains a Hashtable
. However, [PSCustomObject]@{a=1;b=2;c=3}
converts the Hashtable
into a PSObject
. I wasn't able to find documentation stating why this happens.
If you want to convert a Hashtable
into an object in order to use its keys as property names you can use one of the following lines of code:
[PSCustomObject]@{a=1;b=2;c=3}
# OR
New-Object PSObject -Property @{a=1;b=2;c=3}
# NOTE: Both have the type PSCustomObject
If you want to convert a number of Hashtables
into an object where their keys are property names you can use the following code:
@{name='a';num=1},@{name='b';num=2} |
% { [PSCustomObject]$_ }
# OR
@{name='a';num=1},@{name='b';num=2} |
% { New-Object PSObject -Property $_ }
<#
Outputs:
name num
---- ---
a 1
b 2
#>
Finding documentation regarding NoteProperty
was difficult. In the Add-Member
documentation, there isn't any -MemberType
that makes sense for adding object properties other than NoteProperty
. The Windows PowerShell Cookbook (3rd Edition) defined the Noteproperty
Membertype as:
A property defined by the initial value you provide
- Lee, H. (2013). Windows PowerShell Cookbook. O'Reilly Media, Inc. p. 895.