The following object is a toy example to illustrate my question:
$food = @(
[PSCustomObject]@{Category = "Fruits"; Items = @("Mango", "Pineapple", "Orange", "Tangerine")},
[PSCustomObject]@{Category = "Cities"; Items = @("Abidjan", "Douala")},
[PSCustomObject]@{Category = "Animals"; Items = @("Dog", "Bird", "Cat")}
)
$food
Category Items
-------- -----
Fruits {Mango, Pineapple, Orange, Tangerine}
Cities {Abidjan, Douala}
Animals {Dog, Bird, Cat}
I would like to access all items of the "Fruits"
category, for example. I tried to first determine the index of "Fruits"
in the Category
property then use it in the "Items"
property. I thought that the entire array of fruit names would be returned; however, it did not work as expected:
$i = $food.Category.IndexOf("Fruits")
$food[$i]
$food.Items[$i]
Mango
How should I go about it?
Thank you.
You need to apply index $i
to the $food
array itself, not to .Items
:
$food[$i].Items # -> @('Mango', 'Pineapple', 'Orange', 'Tangerine')
However, a more PowerShell-idiomatic solution, using the Where-Object
cmdlet to perform the category filtering (with simplified syntax), would be:
($food | Where-Object Category -eq Fruits).Items
Note:
The above could potentially return multiple elements from the $food
array, namely if more than one element has a .Category
property value 'Fruits'
.
To limit output to the first element, you could append a Select-Object -First 1
pipeline segment, but there's a simpler alternative: the intrinsic .Where()
method has an overload that allows you to stop at the first match:[1]
$food.Where({ $_.Category -eq 'Fruits' }, 'First').Item
Where-Object
cmdlet doesn't have an analogous feature; GitHub issue #13834 suggests enhancing the Where-Object
cmdlet to ensure feature parity with its method counterpart, .Where()
.As for what you tried:
$food.Items[$i]
Accessing .Items
on the array stored in $food
performs member-access enumeration.
That is, the .Items
property on each object stored in the array is accessed, and the resulting values are returned as a single, flat array of all such property values.
To spell it out: $food.Items
returns the following array:
@('Mango', 'Pineapple', 'Orange', 'Tangerine', 'Abidjan', 'Douala', 'Dog', 'Bird', 'Cat')
As an aside: Your index-finding code, $food.Category.IndexOf("Fruits")
, also makes use of member-access enumeration, relying on returning the .Category
property values across all elements of the $food
array.
Indexing into the resulting array (with [$i]
, with $i
being 0
) then only extracts its first element, i.e. 'Mango'
.
[1] Using the .Where()
method has additional implications: (a) the input object(s) must be collected in full, up front, and (b) what is output is invariably a collection, namely of type [System.Collections.ObjectModel.Collection[psobject]]
- see this answer for details.