private/Controls/Get-PopulatedProperties.ps1
|
function Get-PopulatedProperties { <# .SYNOPSIS Property names with at least one non-empty value across items. Drives default column visibility. #> [CmdletBinding()] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [AllowNull()] [object[]]$Items, [string[]]$PropertyNames ) $populated = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($item in $Items) { if ($null -eq $item) { continue } $propsToCheck = if ($PropertyNames) { $PropertyNames } else { $item.PSObject.Properties.Name } foreach ($propName in $propsToCheck) { if ($populated.Contains($propName)) { continue } if ($propName.StartsWith('_')) { continue } # PSObject.Properties[name] resolves the member without invoking its getter, so a missing member is an explicit skip instead of a silent $null read. The read stays in the try but getters can still throw (eg Process.MainModule on elevated procs). $prop = $item.PSObject.Properties[$propName] if (!$prop) { continue } $value = $null try { $value = $prop.Value } catch { continue } $hasValue = $false if ($null -ne $value) { if ($value -is [string]) { # '[Access Denied]' is the string ConvertTo-SafeDataArray implants when the source property threw. # Treating it as a real value means columns full of "Access Denied" show up as populated; which defeats HasData. $hasValue = ![string]::IsNullOrWhiteSpace($value) -and $value -ne '[Access Denied]' } elseif ($value -is [System.Collections.ICollection]) { $hasValue = $value.Count -gt 0 } else { $hasValue = $true } } if ($hasValue) { [void]$populated.Add($propName) } } if ($PropertyNames -and $populated.Count -eq $PropertyNames.Count) { break } } # Return the HashSet itself. @() and [string[]] still use it fine, and can now makeuse of Contains(). return ,$populated } |