Search code examples
powershelloutputselect-object

Variable number of fields to output in Select-Object cmdlet


Let's say that I have an Obj with data describing multiple entities. I want to output it like this (CSV, HTML, Format-Table, whatever):

Property Code   Property descr.  Entity1  Entity2  Entity3
abs_de234       abs prop for de  132      412      412
abs_fe234       abs prop for fe  423      432      234
...             ...              ...      ...      ...  

I would use something like:

$ObjData | % {Select-Object @{Label = "Property Code"; Expression = {$_.propcode}}, @{Label = "Property Desc."; Expression = {$_.descr}},  @{Label = "Entity1"; Expression = {$_.entity1}}, @{Label = "Entity2"; Expression = {$_.entity2}},@{Label = "Entity3"; Expression = {$_.entity3}} }| Format-Table

But what if my object has variable number of entities? Let's say these properties are all in an array:

$EntityList = @('Entity1', 'Entity2', 'Entity4', 'Entity5', 'Entity5')

How, based on $EntityList I can construct corresponding Select-Object command?

Upd.: Based on Help for Select-Object:

Select-Object
  [-InputObject <PSObject>]
  [[-Property] <Object[]>]
  [-ExcludeProperty <String[]>]
  [-ExpandProperty <String>]
  [-Unique]
  [-Last <Int32>]
  [-First <Int32>]
  [-Skip <Int32>]
  [-Wait]
  [<CommonParameters>]

Does this mean I should be able to just use Select-Object -Property $EntityList?


Solution

  • | % {Select-Object

    Don't use % (the ForEach-Object cmdlet) to pipe to Select-Object - pipe directly to Select-Object.

    @{Label = "Entity1"; Expression = {$_.entity1}}

    Unless you need to change the case of the label (property) name, just pass entity1 to Select-Object.

    As with any cmdlet parameter that accepts an array of objects, you're free to pass the array either as an array literal (with the elements enumerated one by one with ,) or as a previously constructed array passed via a variable:

    # Properties that need renaming.
    # Note: Unless you need to *transform* the input property value,
    #       you don't strictly need a *script block* ({ ... }) and can use
    #       a *string* with the property name instead.
    #       E.g., instead of {$_.propcode} you can use 'propcode'
    $propDefs = 
      @{Label = "Property Code"; Expression = {$_.propcode}}, 
      @{Label = "Property Desc."; Expression = {$_.descr}}
    
    # Add properties that can be extracted as-is:
    $propDefs += 'Entity1', 'Entity2', 'Entity4', 'Entity5', 'Entity5'
    
    # Note: Passing the array *positionally* implies binding to the -Property parameter.
    $ObjData | Select-Object $propDefs # add Format-Table, if needed, for display formatting
    

    To demonstrate:

    # Sample input object
    $ObjData = [pscustomobject] @{
      propcode = 'pc'
      descr = 'descr'
      Entity1 = 'e1'
      Entity2 = 'e2'
      Entity3 = 'e3'
      Entity4 = 'e4'
      Entity5 = 'e5'
    }
    
    $propDefs = 
      @{Label = "Property Code"; Expression = {$_.propcode}}, 
      @{Label = "Property Desc."; Expression = {$_.descr}}
    
    $propDefs += 'Entity1', 'Entity2', 'Entity3', 'Entity4', 'Entity5'
    
    $ObjData | Select-Object $propDefs
    

    The above yields:

    Property Code  : pc
    Property Desc. : descr
    Entity1        : e1
    Entity2        : e2
    Entity3        : e3
    Entity4        : e4
    Entity5        : e5