0

I'm trying to combine two arrays into an array of arrays in PowerShell. Here's my script:

$list1 = 'A','B';
$list2 = '1','2';

$result = foreach ($list1Item in $list1)
{
    foreach ($list2Item in $list2)
    {
        ($list1Item, $list2Item)
    }
}

foreach ($resultItem in $result)
{
    Write-Host "$($resultItem[0])-$($resultItem[1])"
}

The result I was expecting was:

A-1
A-2
B-1
B-2

The result I'm actually getting is:

A-
1-
A-
2-
B-
1-
B- 
2-

What am I doing wrong?

Martin Brown
  • 24,692
  • 14
  • 77
  • 122
  • 2
    PowerShell's pipeline automatically unrolls arrays, so nested arrays are almost always a pain to work with. Sure you don't want an array of objects with two properties each? – Mathias R. Jessen Jun 26 '23 at 12:20
  • It's somewhat an academic exercise so I was hoping to understand the theory of how unrolling works. – Martin Brown Jun 26 '23 at 12:21
  • you would need to add a comma there for this to work `, ($list1Item, $list2Item)` wraps the array in an outer array that is then consumed – Santiago Squarzon Jun 26 '23 at 12:26
  • 1
    Using this common [`Join-Object Module`](https://www.powershellgallery.com/packages/JoinModule) (see also: [In Powershell, what's the best way to join two tables into one?](https://stackoverflow.com/a/45483110/1701026)): `$List1 |CrossJoin $List2 | Foreach-Object { $_ -Join '-' }` – iRon Jun 26 '23 at 14:21

1 Answers1

3

PowerShell's default behavior is to unroll enumerable output, which is why you end up with a "flat" array.

You can circumvent this behavior by using Write-Output -NoEnumerate:

$list1 = 'A','B';
$list2 = '1','2';

$result = foreach ($list1Item in $list1)
{
    foreach ($list2Item in $list2)
    {
        Write-Output ($list1Item, $list2Item) -NoEnumerate
    }
}

Another option is to construct a literal nested array - then the pipeline processor will unroll the first dimension and you're left with array objects:

$list1 = 'A','B';
$list2 = '1','2';

$result = foreach ($list1Item in $list1)
{
    foreach ($list2Item in $list2)
    {
        ,@($list1Item, $list2Item)
      # ^
      # prefixed comma acts as a unary array operator
    }
}

Or you can construct something that isn't enumerable, like an object for each pair:

$list1 = 'A','B';
$list2 = '1','2';

$result = foreach ($list1Item in $list1)
{
    foreach ($list2Item in $list2)
    {
        # this creates a scalar object, nothing to unroll here
        [pscustomobject]@{
          Item1 = $list1Item
          Item2 = $list2Item
        }
    }
}

foreach ($resultItem in $result)
{
    Write-Host "$($resultItem.Item1)-$($resultItem.Item2)"
}

See the about_Pipelines help topic for further discussion on this behavior

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206