1

I have the following:

$roles = 'role1;role2;role3;role4' -split ';'
$members = 'member1,member2;;member3;member4' -split ';'

this basically means, for every semicolon, its a new "index".

parsed, this should generate the following output:

Role  Members
----  -------
role1 member1,member2
role2
role3 member3
role4 member4

i want to access objects by name, i am using hashtable, but for some reason, its not working properly.

$roleMembers = @{}

for ($i = 0; $i -lt $roles.Count; $i++) {
  # `Where Length -ne 0` to filter out empty strings
  $roleMembers[$roles[$i]] = $members[($i*2)..($i*2+1)] | Where Length -ne 0
}

PS> $roleMembers outputs

Name                           Value
----                           -----
role1                          member1,member2
role3
role4
role2                          {member3, member4}

how come the output is incorrect? only role 1 is correct, while role3 and role4 are incorrectly empty, and role2 should be empty but isntead it has members3 and member4.

the expected result should be:

Role  Members
----  -------
role1 member1,member2
role2
role3 member3
role4 member4

also, i want the column names to be Role , Members instead of Name, Value respectively

Cataster
  • 3,081
  • 5
  • 32
  • 79

3 Answers3

3

Use the counter $i to get the right element (no need to make any calculations here), remove the where-filter and make it a PSCustomObject to get the role and members properties.

    $roles = 'role1;role2;role3;role4' -split ';'
    $members = 'member1,member2;;member3;member4' -split ';'


    for ($i = 0; $i -lt $roles.Count; $i++) {
            [PSCustomObject]@{
                    Roles   = $roles[$i]
                    Members = $members[$i]
            }
    }

This produces the expected output

    Roles Members        
    ----- -------
    role1 member1,member2
    role2
    role3 member3
    role4 member4
vrdse
  • 2,899
  • 10
  • 20
  • nice! i am setting $var to get the result of the for pscutomobject stored in a variable to access properties. I can do something like: $var.roles[1], and i get role2. i can do $var.members and i get the list of members. but how can i access members for a certain role? for example, $var.Roles[0].Members doesnt output anything (member1, member2), nor does accessing it by name work: $var.Roles['role1'].Members – Cataster Aug 01 '19 at 18:49
  • By index `$var[0]` or a where-filter `$var | Where-Object {$_.Roles -eq 'role1'}` – vrdse Aug 01 '19 at 18:52
  • 3
    @mklement0 The OP also asked that it have column headers of 'Role' and 'Members', which implies custom objects. I think a combination of the two is likely in order. a hashtable with the roles as keys, and custom objects as the values. – TheMadTechnician Aug 01 '19 at 18:57
  • @TheMadTechnician: I cleaned up my original comments, so just to provide context (again): This answer covers the column-headers aspect of the question, but not the access-by-role-name aspect, whereas the opposite is true of Esperento57's answer. Your proposed solution is a way of covering both aspects, though for having specific column names merely _for display_ a more lightweight alternative is to stick with a simple (ordered) hashtable and just use `Format-Table` to provide for-display output with custom column names. My answer now shows both solutions. – mklement0 Aug 05 '19 at 20:32
2

Just do it :

$roleMembers = [ordered]@{}

for ($i = 0; $i -lt $roles.Count; $i++) {
  $roleMembers[$roles[$i]] = $members[$i]
}
Esperento57
  • 16,521
  • 3
  • 39
  • 45
2

Your question has two unrelated requirements:

  • To create an (ordered) data structure that maps roles to members that supports direct lookup by name.

  • To provide tabular output of the resulting data structure with specific column names.

    • vrdse's helpful answer covers that aspect, by creating an array of custom objects with properties named Roles and Members, which PowerShell automatically uses as column headers when it prints custom objects to the screen.

There are two ways to address both requirements:


Solution A: If the custom column names are needed for display only:

Pipe the $roleMembers hashtable from Esperento57's answer to Format-Table and provide the desired column names via calculated properties, which in this case effectively rename the hashtable entries' Key property to Roles, and Value to Members:

PS> $roleMembers | Format-Table @{ n='Role'; e='Key' }, @{ n='Members'; e='Value' }

Role  Members
----  -------
role1 member1,member2
role2
role3 member3
role4 member4

Note that Format-Table output, as with all Format-* cmdlets, is suitable for display only, not for later programmatic processing.


Solution B: If you want to keep objects around with properties named Role and Members for later processing:

As suggested by TheMadTechnician, combine the two solutions by making the hashtable values contain custom objects rather than just the list of members:

$roles = 'role1;role2;role3;role4' -split ';'
$members = 'member1,member2;;member3;member4' -split ';'

$rolesAndMembers = [ordered] @{}
foreach ($i in 0..($roles.Count-1)) {
  $rolesAndMembers[$roles[$i]] = [pscustomobject] @{
    Role = $roles[$i]
    Members = $members[$i]
  }
}

# Outputting .Values enumerates the custom objects stored in the hashtable.
# Because the custom objects have fewer than 5 properties, Format-Table is
# implied for output formatting, and the custom objects' property names
# are used as column headers.
$rolesAndMembers.Values

This yields the same output as above.

Note that the implication is that in order to access, say, role1's members, you'll have to use:

$rolesAndMembers.role1.Members # -> 'member1,member2'
# or: $rolesAndMembers['role1'].Members

As an aside: if you wanted to store members individually rather than as a string list, -split them on custom-object creation:

[pscustomobject] @{
    Role = $roles[$i]
    Members = $members[$i] -split ','  # Members becomes an array with member names
}

$rolesAndMembers.role1.Members[0] would then yield member1, for instance, and $rolesAndMembers.role1.Members[1] would yield member2.


As for what you tried:

$members[($i*2)..($i*2+1)] accesses 2 array elements at an index that is twice that of $i.

However, your $roles and $members arrays have a one-to-one correspondence: they have the same number of elements and for all indices the values directly relate to each other; therefore $members[$i] must be used.

mklement0
  • 382,024
  • 64
  • 607
  • 775