How to get a list of a particular field from a list of objects in powershell

12.1k views Asked by At

I am new to powershell scripting. Apologies if I am missing something simple.

Let's say I have an object called object that has a field called field. Now let there be a list of these objects.

How would I get the list of fields in the same order?

In python it would be:

list_of_objects = [o1, o2, o3]
list_of_fields = [i.field for i in list_of_object]
2

There are 2 answers

5
Maximilian Burszley On BEST ANSWER

is nice, and not so nice, because it unwraps collections for you, and sometimes this can hide that it is masking the elements' members. When you're using $parents.item, you're accessing the array's method, and trying to access its members (which there aren't any, so is giving you $null):

Item           ParameterizedProperty System.Object IList.Item(int index) {get;set;}

You can overcome this by using the method I shared in the comments to iterate over each member and avoid this masking:

$list = $parents | ForEach-Object -MemberName item
$list.registration.parentCompoundNumber

Alternatively, a syntax more people are familiar with:

$list = $parents | Select-Object -ExpandProperty item

or unrolling it yourself:

# you could directly assign the outputs of a `foreach` loop to a variable by
# removing these comments (<##>)
<# $items = #> 
  foreach ($parent in $parents) {
    $parent.item.registration.parentCompoundNumber
  }

To see when this masking is happening, consider this example which uses the unary array operator:

, @('a', 'b', 'c') | Get-Member

This will let you observe the wrapping array or collection's members.

0
mklement0 On

To complement Maximilian Burszley's helpful answer:

The linked answer contains viable workarounds for the member-name collisions; let me add a PSv4+ alternative that is both more concise and faster than a pipeline-based approach:

$parent.ForEach('item').registration.parentCompoundNumber

Using the .ForEach() array method with a property name ('item') unambiguously targets the elements' members.


To offer a slight reframing of the explanation for why a workaround is needed:

  • PowerShell's member-access enumeration essentially treats $someCollection.someProp as if you had written foreach ($element in $someCollection) { $element.someProp }; that is, the elements of $someCollection are enumerated, and the the elements' .someProp property values are returned as an array.

    • Note: As in the pipeline, if the collection happens to have just one element, that element's property value is returned as-is, not as a single-element array.
  • However, if the collection type itself happens to have a member named someProp, it is used, and no enumeration takes place; that is, collection-level members shadow (take precedence over) element-level members of the same name - and that is what happened with .Item in your case.

    • When in doubt, output $someCollection.someProp interactively / during debugging to see what it evaluates to.