1
$vms = @()

$foo = New-Object -TypeName PSObject
$foo | Add-Member -MemberType NoteProperty -Name "Name" -Value "Host1"
$foo | Add-Member -MemberType NoteProperty -Name "OSDisk" -Value "64 GB"
$vms += $foo

$bar = New-Object -TypeName PSObject
$bar | Add-Member -MemberType NoteProperty -Name "Name" -Value "Host2"
$bar | Add-Member -MemberType NoteProperty -Name "OSDisk" -Value "64 GB"
$bar | Add-Member -MemberType NoteProperty -Name "DataDisk1" -Value "128 GB"
$vms += $BAR

$vms[0] | ft
$vms[1] | ft
$vms | ft

As you can see, the second row has more properties than the first row. When displaying, I get only the properties present in the first row, the others are ignored.

PS C:> $vms[0] | ft

Name  OSDisk
----  ------
Host1 64 GB

PS C:> $vms[1] | ft

Name  OSDisk DataDisk1
----  ------ ---------
Host2 64 GB  128 GB

PS C:> $vms | ft

Name  OSDisk
----  ------
Host1 64 GB
Host2 64 GB

What am I hoping to achieve is to have every property in every row:

Name  OSDisk  DataDisk1
----  ------  ----------
Host1 64 GB    
Host2 64 GB    128 GB

[edit] The properties may vary, in my case a VM may have one or more datadisks.

ecstrim
  • 61
  • 1
  • 6
  • `$vms | Select-Object Name, OSDisk, DataDisk1 | Format-Table` – Ansgar Wiechers Nov 18 '19 at 13:09
  • the 1st object in the collection determines what will be displayed. if you send the collection to a CSV file, the 1st object determines what all the other objects will have - extra props are lost entirely. – Lee_Dailey Nov 18 '19 at 13:16
  • @Lee_Dailey how do I avoid that? Thanks – ecstrim Nov 18 '19 at 13:23
  • @ecstrim - it looks like `Theo` has shown how to do it ... find the item with the most properties and sort the collection to send that one 1st. [*grin*] – Lee_Dailey Nov 18 '19 at 13:35

1 Answers1

2

Some time ago I made a helper function to complete the first item in an array of objects with all headers found:

function Complete-ObjectHeaders {
    # function to add properties to the first item in a collection of PSObjects
    # when this object is missing properties from items further down the array.
    # you may need this is you have such a collection and want to export it
    # to Csv, since Export-Csv only looks at the first item to create the
    # csv column headers.
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [PSObject[]]$Collection,

        [int]$MaxItemsToTest = -1,  # < 0 --> test all items in the collection
        [switch]$SortHeaders
    ) 

    # Try and find all headers by looping over the items in the collection.
    # The headers will be captured in the order in which they are found. 
    if ($MaxItemsToTest -gt 0) {
        $MaxItemsToTest = [math]::Min($MaxItemsToTest, $Collection.Count)
        $headers = for($i = 0; $i -lt $MaxItemsToTest; $i++) {
            ($Collection[$i].PSObject.Properties).Name
        }
        $headers = $headers | Select-Object -Unique
    }
    else {
        $headers = $Collection | ForEach-Object {($_.PSObject.Properties).Name} | Select-Object -Unique
    }

    if ($SortHeaders) { $headers = $headers | Sort-Object }

    # update the first object in the collection to contain all headers
    $Collection[0] = $Collection[0] | Select-Object $headers

    ,$Collection
}

In your case, you could do this

Complete-ObjectHeaders -Collection $vms | Format-Table -AutoSize

to output

Name  OSDisk DataDisk1
----  ------ ---------
Host1 64 GB           
Host2 64 GB  128 GB

Hope that helps

Theo
  • 57,719
  • 8
  • 24
  • 41