2

What is the easy and efficient way to transform an array using PowerShell? An initial array has two properties Number and Color.

Number  Color
1       Red
2       Red
3       Red
4       Red
5       Blue
6       Blue
7       Green
8       Green
9       Green

An output array should like:

Color   Number
Red     1,2,3,4
Blue    5,6
Green   7,8,9
dod
  • 67
  • 1
  • 7
  • 2
    Have you looked at `Group-Object`? That's a pretty common cmdlet for exactly this purpose. Also, why would `5` and `6` be grouped under both `Red` and `Blue` in the output? – Lance U. Matthews Apr 28 '20 at 22:09
  • I couldn't get the needed output using Group-Object. it's an error - 5,6 should be in blue only. – dod Apr 28 '20 at 22:25

2 Answers2

4
# Input objects.
$objects = @'
Number,Color
1,Red
2,Red
3,Red
4,Red
5,Blue
6,Blue
7,Green
8,Green
9,Green
'@ | ConvertFrom-Csv

$objects |
  Group-Object Color |
    Select-Object @{ n='Color'; e='Name'}, @{ n='Number'; e={ $_.Group.Number } }

The above yields:

Color Number
----- ------
Blue  {5, 6}
Green {7, 8, 9}
Red   {1, 2, 3, 4}

Note: Due to how Group-Object works, the Color values are sorted alphabetically in the output.

See also:

mklement0
  • 382,024
  • 64
  • 607
  • 775
1

Adding to @mklement0's great answer using Group-Object, we can also group the items into a System.Collections.Hashtable by adding the -AsHashTable switch. To iterate the hashtable keys and values, we have to use System.Collections.Hashtable.GetEnumerator.

$objects = @'
Number,Color
1,Red
2,Red
3,Red
4,Red
5,Blue
6,Blue
7,Green
8,Green
9,Green
'@ | ConvertFrom-Csv

$ht = $objects | Group-Object -Property Color -AsHashTable

$ht.GetEnumerator() | 
    Select-Object @{Name="Color";Expression={$_.Key}}, @{Name="Number";Expression={$_.Value | Select-Object -ExpandProperty Number}}

Output:

Color Number
----- ------
Green {7, 8, 9}
Red   {1, 2, 3, 4}
Blue  {5, 6}

If we want to sort your result by Number, we can use Sort-Object to sort by the first number:

$ht.GetEnumerator() | 
    Select-Object @{Name="Color";Expression={$_.Key}}, @{Name="Number";Expression={$_.Value | Select-Object -ExpandProperty Number}} |
        Sort-Object @{Expression={$_.Number[0]}}

Or use an System.Collections.Specialized.OrderedDictionary to create an ordered hashtable of arrays and maintain order of key insertion:

$ht = [ordered]@{}
foreach ($object in $objects) {
    if (-not ($ht.Keys -contains $object.Color)) {
        $ht[$object.Color] = @()
    }
    $ht[$object.Color] += $object.Number
}

$ht.GetEnumerator() | 
    Select-Object @{Name="Color";Expression={$_.Key}}, @{Name="Number";Expression={$_.Value}} 

Which will output the following:

Color Number
----- ------
Red   {1, 2, 3, 4}
Blue  {5, 6}
Green {7, 8, 9}
RoadRunner
  • 25,803
  • 6
  • 42
  • 75