I have an array of hashtables ($workObjectFromFile) that contain a single string 'id' and an array 'roles'. I want to return each row/object in the ht whose roles array matches an input array.
# generate workobject [System.Collections.Hashtable]
$workObject = @()
$workObject = @{id = 'server1'; roles = 'role1'}
$workObject = @{id = 'server2'; roles = 'role1','role2'}
$workObject = @{id = 'server3'; roles = 'role4','role5','role2'}
$json_string = ConvertTo-Json $workObject -Depth 20
$jsonFile = "C:\scratch\hostmap.json"
Set-Content $jsonFile -Value $json_string -Force
$json_fromFile = Get-Content $jsonFile -Raw
$workObjectFromFile = ConvertFrom-Json $json_fromFile
# find and return each server with any one of the given roles in the input filter
$inputFilter = 'role1','role3'
$return = @()
$workObjectFromFile | where {$_.roles -in $inputFilter} | foreach {
$return = [ordered]@{
id = $_.id
roles = $_.roles
}
}
return $return
Why does the where clause above only return server1? if i change this to
$workObjectFromFile.roles | where {$_ -in $inputFilter}
It will return only the roles, but for the correctly matched objects
I need to return the parent object for these matched nested arrays
CodePudding user response:
server1.role
is a string, so for that item your code is equivalent to this:
PS> 'role1' -in @('role1', 'role3')
True
whereas the roles for server2
and server3
are arrays, so your code is doing this for them:
PS> @('role1', 'role2') -in @('role1', 'role3')
False
i.e. does the entire array @('role1', 'role2')
appear as a single item in the array @('role1', ‘role3')
?
What you actually want to ask is:
- are there any items in
@('role1', 'role2')
that also appear as an item in@('role1', 'role3')
which you can do as follows (adapted from https://stackoverflow.com/a/18845506/3156906):
PS> Compare-Object @('role1', 'role2') @('role1', 'role3') -PassThru -IncludeEqual -ExcludeDifferent
role1
Putting this back into your code gives:
PS> $workObjectFromFile | where { Compare-Object $inputFilter $_.roles -PassThru -IncludeEqual -ExcludeDifferent }
roles id
----- --
role1 server1
{role1, role2} server2
As an aside, if you want to make server1.roles
an array containing a single item you can do one of these:
Comma operator
PS> $workObject = @{id = 'server1'; roles = ,'role1'}
# ^ comma operator
Array subexpression operator
PS> $workObject = @{id = 'server1'; roles = @( 'role1' )}
# ^^ ^ array subexpression operator
CodePudding user response:
First of all, I recommend you to use the PowerShell pipeline to accumulated you objects because it hash a better PowerShell syntax and because the increase assignment operator ( =
) might perform very badly when the collection gets larger:
$workObject =
@{ id = 'server1'; roles = 'role1' },
@{ id = 'server2'; roles = 'role1','role2' },
@{ id = 'server3'; roles = 'role4','role5','role2' }
For what you trying to do, is actually issued here: #2132
The -Like and -NotLike comparison operators should allow for an array of values (-AnyIn
). Unfortunately that doesn't exist (yet?) knowing that it does actually work that way for comparison operators that usually take a (simple) scalar at the righthand side, see: comparison operators / common features:
When the input of an operator is a scalar value, the operator returns a Boolean value. When the input is a collection, the operator returns the elements of the collection that match the right-hand value of the expression. If there are no matches in the collection, comparison operators return an empty array.
In other words, to resolve this, you will need to to iterate to the elements (as suggested in @Daniel's comment, but at the other hand might actually use the -eq
operator for this:
$return = $workObject | Where-Object { $_.roles.where{ $inputFilter -eq $_ } }