Chris Rodriguez
Chris Rodriguez

Reputation: 918

Powershell - Group-Object PSObject with multiple properties

I'm trying to take an array of PSObjects similar to

 @{BakId=27; Name=DB_A; Lsn=123; File=A_01; Size=987}
 @{BakId=28; Name=DB_B; Lsn=456; File=B_01; Size=876}
 @{BakId=28; Name=DB_B; Lsn=456; File=B_02; Size=765}
 @{BakId=28; Name=DB_B; Lsn=456; File=B_03; Size=654}

And create a new grouped object that removes redundant header info.

 BakId  Lsn  Name  Files
 27     123  DB_A  {@{File=A_01.bak;Size=777}}
 28     456  DB_B  {@{File=B_01.bak;Size=888}, @{File=B_02.bak;Size=999}, ...}

I tried using group-object but can only get it to work for one property. (all grouped properties go into Group.Name as a a string of comma separated values.)

This is the best I've come up with, but feels hacky.

$list | Group-Object -Property BakId | % {
   $BakId = $_.Name
   $Lsn   = $_.Group[0].Lsn  # <--- is there a better way than this?
   $Name  = $_.Group[0].Name # <--- Ditto
   $Files = $_.Group | Select-Object -Property SequenceNumber, Size
   Write-Output (New-Object -TypeName PSObject -Property @{ BakId=$BakId;Files = $Files })
}

Is there a better way?

Thanks

Upvotes: 1

Views: 6834

Answers (1)

mklement0
mklement0

Reputation: 437111

You can simplify the approach to constructing the output objects by using a single Select-Object call with calculated properties, and, relying on the order of the grouping properties, access their group-specific values via the .Values collection:

$list | Group-Object -Property BakId, Lsn, Name | 
  Select-Object @{n='BakId'; e={ $_.Values[0] }}, 
                @{n='Lsn';   e={ $_.Values[1] }},
                @{n='Name';  e={ $_.Values[2] }},
                @{n='Files'; e={ $_.Group | Select-Object File, Size }}

Note:

  • $_.Values[<ndx>] takes the place of $_.Group[0].<name> in your approach; note that the latter only makes sense for the actual grouping properties, because any others will not be uniform throughout the group.

  • The .Values collection ([System.Collections.ArrayList]) on each group object output from Group-Object ([Microsoft.PowerShell.Commands.GroupInfo] instance) contains that group's shared grouping-property values as-is (original type), in the order specified.
    By contrast, property .Name contains the stringified combination of all grouping-property values: a string containing a comma-separated list.
    Unfortunately, such details are currently missing from Get-Help Group-Object.


If you wanted to avoid having to repeat the grouping properties (e.g., in preparation for writing a function wrapper; PSv3+):

$props = 'BakId', 'Lsn', 'Name'
$list | Group-Object -Property $props | ForEach-Object {
   $propDefs = [ordered] @{}
   foreach ($i in 0..($props.Length-1)) { $propDefs.[$props[$i]] = $_.Values[$i] }
   $propDefs.Files = $_.Group | Select-Object File, Size
   New-Object PSCustomObject -Property $propDefs
}

Upvotes: 7

Related Questions