Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select item from PowerShell hashtable without using Foreach

Tags:

powershell

I have this list of PSObjects, each of which contains a Hashtable. Currently I can get it out like this:

foreach ($item in $myListOfItems) { $item.Metadata["Title"] }

However, I am wondering if I can do it somehow with piping and Select. Is this possible? For example:

$myListOfItems | Select $_.Metadata["Title"] ...which only outputs a whole bunch of blank lines :(

Any ideas? Thanks so much in advance!

like image 637
Ben H Avatar asked Jun 13 '26 11:06

Ben H


2 Answers

What about

$myListOfItems | select @{ Label="Title";Expression={$_.Metadata["Title"]}}    
like image 82
Martin Avatar answered Jun 17 '26 01:06

Martin


The original idea was close but just got the syntax wrong. To use $_, the expression must be within a script-block parameter (Powershell V3+):

 $myListOfItems | select { $_.Metadata["title"] }

Without the enclosing script-block, $_ is evaluated in the context of the whole pipeline instead of the pipeline element (Select-Object in this case). At this level, $_ is not defined so evaluates to $null. PowerShell is somewhat forgiving about using $null inappropriately. In particular, attempting to retrieve a property of $null is not an error, it just returns $null. So $null.Metadata["title"]$null["title"]$null. Select-Object $null just outputs an empty selection object for each input object thus resulting in the blank lines.

It should be noted that, strictly speaking, neither the originally suggested Select-Object (as fixed) nor the hash table version in the answer provide the same output as the ForEach loop. The loop produces a sequence of values from the .Metadata["Title"] members while Select-Object produces a sequence of selection objects [Selected.System.Collections.Hashtable] each containing a member (named $_.Metadata["Title"] and Title respectively) which holds the value. Whether this causes a problem depends on the usage.

For a really short version, try:

$myListOfItems.Metadata.Title

Since PowerShell V3, specifying a non-existent member of a collection causes Powershell to perform a member enumeration whereby it would enumerate the collection and return a sequence of values of the specified member of each element of the collection (assuming there is one). If we assume left to right evaluation then the expression would be evaluated as:

($myListOfItems.Metadata).Title

Firstly enumerating the collection of items, as it has no Metadata member. Each item does have a Metadata member thus resulting in an Array of the Metadata members. Then this collection of Metadata hashtables, having no Title member, is enumerated to produce an Array of the Title elements of the hashtables.

† I think the term member enumeration is somewhat misleading. It is not the members which are being enumerated but the objects in the collection and an Array of the specified member of each object is then created. Indeed, even though the manual about_Hash_Tables states that the hash table has properties named after the keys with corresponding values, this is actually just Powershell shorthand for invoking the indexer [] (which actually appears to override member access so beware of hash tables with keys "Count", "Keys", "Values", ... if you want to access the actual hash table members of these names). So, strictly speaking, there is no 'Title' member. The term "enumerated collection member access" (ECMA ;) would better describe what is happening but after all "A rose by any other name".

like image 22
Uber Kluger Avatar answered Jun 17 '26 01:06

Uber Kluger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!