Cataster
Cataster

Reputation: 3521

How to combine string inputs and generate array dynamically?

param([string]$roles,[string]$members)

Suppose I am passing input on the command line like this:

PS> role1,role2,role3,role4 member1,member2,,,,member3,,member4

The array I expect for this would be:

$array = @(
    @('role1', 'member1,member2'),
    @('role2', ''),
    @('role3', 'member3'),
    @('role4', 'member4')
)

I know to turn string to array:

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

Now how do I combine $roles with $members so that each role will be associated with member(s)? and how wouldIi generate the array dynamically?

Pseudocode:

$array = @()

($roles+$members) | %{
    $role = $_.roles
    if ($_.members) {
        $_.members -split ',,' | ForEach-Object { $array += $role $_ }
    } else {
        $array += $role 
    }
}

Note: I am splitting members as an index of its own for each double comma because apparently semicolons aren't accepted on a command line because they break the command line, so I have to use double comma as delimiter.

Note 2: notice the 4 commas: ,,,, this indicates that role2 does not have members to add, so in essence it means between the 4 commas is no input for member to that index/item (role2), i.e. ,,EMPTY,,.

Upvotes: 1

Views: 133

Answers (3)

mklement0
mklement0

Reputation: 437833

If you really want to stick with this parameter format, you can create the desired output array as follows:

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

$i = 0
$array = @(foreach ($role in $roles) {
  , ($role, $members[$i++])
})

Note that if you pass your arguments from PowerShell, you need to quote them, as PowerShell will otherwise parse them as an array.

And with quoting you're free to use ; in lieu of ,,, for instance, to separate the member groups.

A better way to represent the argument data for later processing is to create an array of custom objects rather than a nested array:

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

$i = 0
$array = @(foreach ($role in $roles) {
   [pscustomobject] @{
     Role = $role
     Members = $members[$i++] -split ','
   }
})

Each object in $array now has a .Role and a .Members property, the latter containing the individual members as a an array of strings.

Alternatively, you could create a[n ordered] hashtable from the input, keyed by role name, but that is only necessary if you need to access roles by name or if you wanted to rule out duplicate roles having been specified.


Here's an alternative argument format that is easier to understand:

$rolesAndMembers = 'role1 = member1,member2 ; role2= ; role3=member3 ; role4=member4'

$array = @(foreach ($roleAndMembers in ($rolesAndMembers -replace ' ' -split ';')) {
  $role, $members = $roleAndMembers -split '='
  [pscustomobject] @{
    Role = $role
    Members = $members -split ','
  }
})

Upvotes: 2

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174485

I'd strongly recommend using hashtables/dictionaries to pass these role mappings:

param(
    [System.Collections.IDictionary]$RoleMembers
)

# now we can access each mapping by role name:
$RoleMembers['role1'] # member1, member2

# or iterate over them like an array:
foreach($role in $RoleMembers.Keys){
    $RoleMembers[$role]
}

You could use one of the construct the input argument from your current input strings:

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

$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
}

Upvotes: 1

Bill_Stewart
Bill_Stewart

Reputation: 24555

Your parameter format is rather bizarre, but here's one way:

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

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

I would recommend redesigning the script to use standard PowerShell parameters (the engineering effort would be worth it, IMO).

Upvotes: 2

Related Questions