7

How do I get properties that ONLY have populated values?

So for example if I run

Get-QADUser -Identity "SomeOne" -IncludeAllProperties

the output would of course include.. all properties, including those with and those without values. I want a listing of properties with values only. How is it done generally speaking?

This wouldn't be restricted to Quest Cmdlets, I only use Get-QADUser as an example.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Andrew Stevens
  • 105
  • 1
  • 2
  • 12

4 Answers4

14

You could try using the built-in (hidden) property of PowerShell objects called PSObject, which includes a property called Properties, i.e. a list of all properties on the parent object.

Maybe easier with an example. Take Get-Process... a process can have many attributes (properties) with or without values. In order to get just the ones with values you do this:

(Get-Process | Select -First 1).PSObject.Properties | ?{$_.Value -ne $null} | FT Name,Value

Note that I limited this to just the first process returned by Get-Process. We then get all the properties defined on that object, filtering where Value is not null and then displaying just the Name and Value for those properties.

Charlie Joynt
  • 4,411
  • 1
  • 24
  • 46
  • 1
    This is a better approach :) – Vesper Jun 05 '17 at 14:06
  • @Vesper: Specifically, this answer's approach is more concise, performs better (because no `Get-Member` call is needed), and implicitly also includes `NoteProperty` members. The `.psobject.properties` approach would also make it easier to exclude (rarely seen) write-only properties: `?{ $_.IsGettable -and $_.Value -ne $null }` – mklement0 Jun 05 '17 at 16:54
  • You might want to exclude empty strings too. `''` – js2010 Jul 22 '23 at 13:26
10

To complement Charlie Joynt's helpful answer:

Below is convenience function Remove-NullProperties, which creates custom-object copies of its input objects populated with only the non-$null properties of the input objects.

Example use:

# Sample input collection, with 2 objects with different $null-valued
# properties.
$coll = [pscustomobject] @{ one = 'r1c1'; two = $null; three = 'r1c3' },
        [pscustomobject] @{ one = 'r2c1'; two = 'r2c2'; three = $null }

# Output copies containing only non-$null-valued properties.
# NOTE: The `ForEach-Object { Out-String -InputObject $_ }` part is solely
#       there to ensure that *all* resulting properties are shown.
#       With the default output, only the properties found on the FIRST
#       input object would be used in the output table.
$coll | Remove-NullProperties | 
  ForEach-Object { Out-String -InputObject $_ }

This yields the following - note how the respective null-valued properties were removed:

one  three
---  -----
r1c1 r1c3 


one  two 
---  --- 
r2c1 r2c2

Remove-NullProperties source code:

<#
.SYNOPSIS
Removes properties with $null values from custom-object copies of 
the input objects.

.DESCRIPTION
Note that output objects are custom objects that are copies of the input
objects with copies of only those input-object properties that are not $null.

CAVEAT: If you pipe multiple objects to this function, and these objects
        differ in what properties are non-$null-valued, the default output
        format will show only the non-$null-valued properties of the FIRST object.
        Use ... | ForEach-Object { Out-String -InputObject $_ } to avoid
        this problem.

.NOTES
Since the output objects are generally of a distinct type - [pscustomobject] -
and have only NoteProperty members, use of this function only makes sense
with plain-old data objects as input.

.EXAMPLE
> [pscustomobject] @{ one = 1; two = $null; three = 3 } | Remove-NullProperties

one three
--- -----
  1     3

#>
function Remove-NullProperties {

  param(
    [parameter(Mandatory,ValueFromPipeline)]
    [psobject] $InputObject
  )

  process {
    # Create the initially empty output object
    $obj = [pscustomobject]::new()
    # Loop over all input-object properties.
    foreach($prop in $InputObject.psobject.properties) {
      # If a property is non-$null, add it to the output object.
      if ($null -ne $InputObject.$($prop.Name)) {
        Add-Member -InputObject $obj -NotePropertyName $prop.Name -NotePropertyValue $prop.Value
      }
    }
    # Give the output object a type name that reflects the type of the input
    # object prefixed with 'NonNull.' - note that this is purely informational, unless
    # you define a custom output format for this type name.
    $obj.pstypenames.Insert(0, 'NonNull.' + $InputObject.GetType().FullName)
    # Output the output object.
    $obj
  }

}
mklement0
  • 382,024
  • 64
  • 607
  • 775
3

These answers didn't work for me in the case of importing an object from an Infoblox csv file. Some values were the empty string, but not null. Testing whether a property is true or not, seems to work better for me. And the result is an object.

$a = [pscustomobject]@{one='hi';two='';three='there'}
$prop = $a.psobject.Properties | where value | foreach name
$a | select $prop

one three
--- -----
hi  there
Mike Pennington
  • 41,899
  • 19
  • 136
  • 174
js2010
  • 23,033
  • 6
  • 64
  • 66
  • 1
    I really like the simplicity of this approach. While it might not catch every variation of null, blank, etc.; I will definitely start using this idea in interactive coding. I think its very handy for getting your head around new datasets. – jswanson Dec 03 '21 at 17:29
  • For objects imported from a CSV file, this works (CSV field values are all _strings_), but beware of objects that have _numeric_ properties with value `0`, which would be excluded too. – mklement0 Aug 03 '22 at 19:10
0

You first get its properties (since Get-QADUser depends on AD schema, the properties list is dynamic) with get-member -type property, then filter out those that don't have \{.*(get).*\} in its definition (that is, they are not "gettable"), then enumerate the resultant list by name and filter out nulls.

$someone=Get-QADUser -Identity "SomeOne" -IncludeAllProperties
$members=$someone|get-member -type property| where {$_.definition -match '\{.*(get).*\}'}
foreach ($member in $members) {
    if ($someone[$member.name] -ne $null) {
        write-host $member.name $someone[$member.name] 
    }
}
Vesper
  • 18,599
  • 6
  • 39
  • 61
  • Kudos for trying to exclude write-only properties, although chances of running into them in a PowerShell context are slim; `-match '\{get;'` would do. If you wanted to generalize this to include `NoteProperty` members, use `-Type Properties` (plural), but there the `-match` test wouldn't work (and isn't needed). To promote good habits, can I suggest you [avoid the use of `Write-Host`](http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful/) and use something like `$member.name + ': ' + $someone[$member.name]` as the output statement? – mklement0 Jun 05 '17 at 16:40