Not all properties displayed
Asked Answered
I

1

9

When we're trying to export data to other functions via the pipeline, we observe some strange behavior in PowerShell.

Example code:

$Array = @()

$Obj1 = [PSCustomObject]@{
    Member1   = 'First'
    Member2   = 'Second'
}

$Obj2 = [PSCustomObject]@{
    Member1   = 'First'
    Member2   = 'Second'
    Member3   = 'Third'
}

$Array = $Obj1, $Obj2
$Array | Out-GridView -Title 'Not showing Member3'

$Array = $Obj2, $Obj1
$Array | Out-GridView -Title 'All members correctly displayed'

In the example above you can see that when the first object only contains 2 properties, the Out-GridView CmdLet (and others) only show 2 properties, even though the second object has 3 properties. However, when the first object in the array has 3 properties it does display them all correctly.

Is there a way around this? Because it's not possible to predict up front how many properties on an object there will be and if the object with the most properties will be the first one in the array.

Integrator answered 8/6, 2017 at 6:28 Comment(8)
"Because it's not possible to predict up from how many properties on an object there will be" - uhh .. yes it is, you control what the object is, you control what properties you put in it. They're not random.. select the ones you want, on every object, and then it will work ..Kymric
True, but the thing is my code generates extra proporties depending om some logic. So some objects only have 2 and others might have 15..Integrator
I think, powershell just thinks alright, I can predict [PSCustomObjects] with same props are coming - lets stop looking at it after the first one. You should be able to go along by setting up all objects the same way. Technically, you're returning an [ADUser object and a [DateTime] and are surprised that the results are strange.Fortdefrance
"True, but the thing is my code generates extra proporties depending om some logic. So some objects only have 2 and others might have 15." - you can tell this is a bad design by the way it's not working well. :-| Data model has fields you care about, if one instance has no value for that field you leave the value blank. e.g. in a CSV you can leave some column empty in a row but you can't take a column out of a row, the whole CSV breaks if you do.Kymric
This is the function that generates the properties. You can see that it doesn't know how many properties there will be from the start. If this is bad design I have to rethink how to add the empty properties in the end. Something like $Array | ForEach-Object {$_.PSObject.Properties.Name} | Sort-Object -UniqueIntegrator
I think, thats what you have to go with: Create an array with all possible properties, and use this at Select-Object. It works on your example.Fortdefrance
"This is the function that generates the properties" - you're trying to smush a tree structure into a list, and it's going to be awful to work with - you won't know how many memberN properties there are to deal with. Wouldn't you make one property for the leaf node ('name'='jake'), and one property, an array, with 'path' ('group1', 'group1.1'), and then all the objects look the same, the array is designed to have a varying length of content but you can work with it by one name, and if you want to render it in some kind of list you can do that at display time... etc.Kymric
Thx for the comment and the tips. I'll try to rewrite the function because I think you're right, an array list offers more flexibility.Integrator
D
10

I had the same experience once and created the following reusable 'Union' function:

# 2021-08-25 Removed Union function

Usage:

$Obj1, $Obj2 | Union | Out-GridView -Title 'Showing all members'

It is also supposed to work with complex objects. Some standard cmdlets output multiple object types at once and if you view them (e.g. Out-GridView) or dump them in a file (e.g. Export-Csv) you might miss a lot of properties. Take as another example:

Get-WmiObject -Namespace root/hp/instrumentedBIOS -Class hp_biosSetting | Union | Export-Csv ".\HPBIOS.csv"

Added 2014-09-19:

Maybe this is already between the lines in the comments $Array | Select * | … will not resolve the issue but specifically selecting the properties $Array | Select Member1, Member2, Member3 | … does.
Besides, although in most cases the Union function will work, there are some exceptions to that as it will only align the first object with the rest. Consider the following object:

$List = @(
    New-Object PSObject -Property @{Id = 2}
    New-Object PSObject -Property @{Id = 1}
    New-Object PSObject -Property @{Id = 3; Name = "Test"}
)

If you Union this object everything appears to be fine and if you e.g. ExportTo-CSV and work with the export .csv file from then on you will never have any issue.

$List | Union
Id Name
-- ----
 2
 1
 3 Test

Still there is a catch as only the first object is aligned. If you e.g. sort the result on Id (Sort Id) or take just the last 2 (Select -Last 2) entries, the Name is not listed because the second object doesn’t contain the Name property:

$List | Union | Sort Id
Id
--
 1
 2
 3

Therefor I have rewritten the Union-Object (Alias Union) function`):

Union-Object

# 2021-08-25 Removed Union-Object function

Syntax:

$Array | Union | Out-GridView -Title 'All members correctly displayed'

Update 2021-08-25

Based on az1d helpful feedback on an error caused by equal property names with different casing, I have created a new UnifyProperties function.
(I will no longer use the name UnionObject for his)

function UnifyProperties {
  $Names = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
  $InputCollected = @($Input)
  $InputCollected.ForEach({ 
    foreach ($Name in $_.psobject.Properties.Name) { $Null = $Names.Add($Name) }
  })
  $inputCollected | Select-Object @($Names)
}

Usage:

[pscustomobject] @{ one = 1; two = 2; three = 3 },
[pscustomobject] @{ ONE = 10; THREE = 30; FOUR = 4 } |
    UnifyProperties

one two three FOUR
--- --- ----- ----
  1   2     3
 10        30 4

See also: #13906 Add -UnifyProperties parameter to Select-Object

Demicanton answered 8/6, 2017 at 7:17 Comment(5)
Love and depend on Union-Object, thank you for creating it. One side effect is constantly getting hundreds of errors Select : The property cannot be processed because the property "example" already exists. $Objects | Select ([String[]]($Property | Select -Unique)) the code still seems to work well, i get the results i expect (no missing columns) but aside from just putting "$Objects | Select ([String[]]($Property | Select -Unique) | out-null" i dont know if there's a reason behind this error showingFiore
I finally found where the bug is in this code. It's with Select -Unique it's case sensitive so if you have multiple properties which are the same word but different casing (eg firefox and FIREFOX) then Select -Unique doesn't always only return one of them github.com/PowerShell/PowerShell/issues/12059 and then $Objects | Select $Property fails because of the two different cased, but same named properties still existing. The fix is to use Sort -Unique instead of Select -UniqueFiore
Even better, I created a unique method that retains the original sort order: ($Property | Group-Object -NoElement).Name | Get-Unique so the full last line would become $Objects | Select ([String[]]($Property | Group-Object -NoElement).Name | Get-Unique)Fiore
Thanks for the feedback, I have updated the answer accordingly.Demicanton
$Names=System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) failed on my server because my PS is an old version (didn't like 'new') This worked though: $Names = New-Object -TypeName 'System.Collections.Generic.HashSet[String]' -ArgumentList ([StringComparer]::InvariantCultureIgnoreCase)Gomar

© 2022 - 2024 — McMap. All rights reserved.