3

I would like to create multidimension array in powershell, I tried this way and it works, but I want to simplified it to make it there is no repetition of name and pn. Anyone can give idea please, Thanks a lot

$array = @(
    [PSCustomObject]@{name = 'PRODCT_1'; pn = 'gra-001'}
    [PSCustomObject]@{name = 'PRODCT_11a'; pn = 'ght-001'}
    [PSCustomObject]@{name = 'PRODCT_ca1'; pn = 'hasha-001'}
    [PSCustomObject]@{name = 'PRODCT_ga1'; pn = '45-001'}
    [PSCustomObject]@{name = 'PRODCT_4a1'; pn = 'aert-001'}
    [PSCustomObject]@{name = 'PRODCT_ata41'; pn = '43-001'}
  ) 
Cheries
  • 834
  • 1
  • 13
  • 32
  • 5
    This is not an example of a multidimensional array. It is a just a standard one-dimensional array made up of objects with multiple properties. – Daniel May 16 '22 at 06:18
  • Why not use a [class](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.2)? – Daniel Mann May 16 '22 at 13:59

3 Answers3

3

Not really simplified but this is one way you could do it using an ordered dictionary and loop through the key / value pairs to create new instances of PSCustomObject:

$dict = [ordered]@{
    'PRODCT_1'     = 'gra-001'
    'PRODCT_11a'   = 'ght-001'
    'PRODCT_ca1'   = 'hasha-001'
    'PRODCT_ga1'   = '45-001'
    'PRODCT_4a1'   = 'aert-001'
    'PRODCT_ata41' = '43-001'
}

And then you can create the instances using either .GetEnumerator():

$dict.GetEnumerator() | ForEach-Object {
    [pscustomobject]@{
        Name = $_.Key
        Pn   = $_.Value
    }
}

Or looping through the Keys:

$dict.PSBase.Keys | ForEach-Object {
    [pscustomobject]@{
        Name = $_
        Pn   = $dict[$_]
    }
}

Lastly, as zett42 suggests you can combine .GetEnumerator() with Select-Object using a calculated property:

$dict.GetEnumerator() | Select-Object Name, @{N='Pn';E={$_.Value}}
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
3

This creates an actual multidimensional array as the basis for generating the array of [pscustomobject]:

# Create multidimensional array
$temp = ('PRODCT_1', 'gra-001'),
        ('PRODCT_11a', 'ght-001'),
        ('PRODCT_ca1', 'hasha-001'),
        ('PRODCT_ga1', '45-001'),
        ('PRODCT_4a1', 'aert-001'),
        ('PRODCT_ata41', '43-001')

# Create single-dimensional array of pscustomobject
$array = $temp | Select-Object @{ n='name'; e={ $_[0] }}, 
                               @{ n='pn';   e={ $_[1] }}

# Output to console
$array

If you prefer, you could also do it using a single statement, without a temporary variable:

$array = (
    ('PRODCT_1', 'gra-001'),
    ('PRODCT_11a', 'ght-001'),
    ('PRODCT_ca1', 'hasha-001'),
    ('PRODCT_4a1', 'aert-001'),
    ('PRODCT_ata41', '43-001')
) | Select-Object @{ n='name'; e={ $_[0] }}, @{ n='pn'; e={ $_[1] }}

An even more succinct alternative using the intrinsic .ForEach method to directly create [PSCustomObject] array elements:

$array = (
    ('PRODCT_1', 'gra-001'),
    ('PRODCT_11a', 'ght-001'),
    ('PRODCT_ca1', 'hasha-001'),
    ('PRODCT_4a1', 'aert-001'),
    ('PRODCT_ata41', '43-001')
).ForEach{ [PSCustomObject]@{ name = $_[0]; pn = $_[1] } }

The latter is also the most efficient way, as it doesn't involve pipeline parameter-binding overhead.

Each of these produce the same output:

name         pn
----         --
PRODCT_1     gra-001
PRODCT_11a   ght-001
PRODCT_ca1   hasha-001
PRODCT_ga1   45-001
PRODCT_4a1   aert-001
PRODCT_ata41 43-001

Explanation for the Select-Object solutions:

For each "row" of $temp, the Select-Object command uses calculated properties to turn the array elements into object properties.

E. g. it takes the row ('PRODCT_1', 'gra-001'), which is an array of two elements. Then @{ n='name'; e={ $_[0] }} gets processed, which assigns the element at index 0 ('PRODCT_1') to property name. Then @{ n='pn'; e={ $_[1] }} gets processed, which assigns the element at index 1 ('gra-001') to property pn.

This process gets repeated for all remaining rows and we get an array of objects stored in $array, just like your original code does.

zett42
  • 25,437
  • 3
  • 35
  • 72
2

Note

My answer keep the array of PSObject from your initial question but remove the name / pn repeat. I think in most cases, keeping the PSObject array structure is better (more flexible).

If you want an actual multidimensional array, Zett42's answer is what you want.


You could use a function or scriptblock invoke. the values will be assigned positionally so you don't have to write the names (name, pn) each times.

Using a function

Function Add-product($Name, $pn) { [PSCustomObject]@{name = $Name; pn = $pn } }
  
  $array = @(
      Add-product 'PRODCT_1' 'gra-001'
      Add-product 'PRODCT_11a' 'ght-001'
      Add-product 'PRODCT_ca1' 'hasha-001'
  )

Using a scriptblock invoke

  $add = {Param($Name,$pn)  [PSCustomObject]@{name = $Name; pn = $pn}}
  $array = @(
      & $add 'PRODCT_1' 'gra-001'
      & $add 'PRODCT_11a' 'ght-001'
      & $add 'PRODCT_ca1' 'hasha-001'
  )

Using CSV formatted data

This one is just another way to do it, although I'd probably never use it in any scenario, since I prefer the flexibility of using a custom function / scriptblock. That being said, it is definitely the most compact.

$array = @'
Name,Pn
PRODCT_1,gra-001
PRODCT_11a,ght-001
PRODCT_ca1,hasha-001
'@ | ConvertFrom-Csv
Sage Pourpre
  • 9,932
  • 3
  • 27
  • 39