Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Comparing PowerShell Property Names

Posted on February 24, 2021February 24, 2021

Recently, I was chatting with my friend Gladys Kravitz about the hassle of comparing objects in PowerShell. Even after all these years. She has a specific use case, but you might also feel the need for a better comparison option. And to be clear, the comparison we're talking about is not the object's values, as you might see with Compare-Object. But rather the property names.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

In Gladys' situation, she imports data from CSV files which she processes through her scripts to get things done. Her use-case is to compare property names. Does this CSV file have the same property names as that CSV file? Are there extra properties? Or is something missing? So let's see how we might address this.

Think Objects

Even though we're starting with CSV files, we're really talking about objects. Gladys can import her CSV files and turn them into custom objects. You might be creating objects to compare through some other mechanism. The bottom line is that we should look at this as an object comparison problem, not a file problem.

Let me start by generating some sample data.

$csvA = @"
samantha,darren-1,darren-2,tabitha,esmerelda
1,2,3,4,5
2,4,6,8,10
3,6,9,11,12
11,22,33,44,55
"@

$csvB = @"
samantha,darren-1,darren-2,esmerelda,gladys,agatha
1,2,3,4,0,9
2,4,6,8,1,8
3,6,9,11,8,0
11,22,33,55,66,77
"@

$a = $csvA | ConvertFrom-Csv
$b = $csvB | ConvertFrom-Csv

For my purposes $A is the reference object and the $B is the difference object. In looking at the data you can see the $B is missing "tabitha" and has extra properties "agatha" and "gladys". I don't care about the values. We even don't really care about the order of property names because one we have an object we're going to reference by property name, not its position in the CSV header.

PSObject

To compare object property names, we don't need the entire object. All we need is one item to analyze because the property names are the same for objects. PowerShell goes to a lot of effort to make it easy to use for IT Pros. PowerShell hides a lot of the .NET sausage-making so you only have to work with the final results. But sometimes, we need to get our hands dirty. In this case, we're going to use a ubiquitous property called PSObject.

We're going to use the Properties property. Here's what it looks like.

It is easy then to create a list of property names for the reference and difference objects.

$refProp = $a[0].psobject.properties.name | Sort-Object
$diffProp = $b[0].psobject.properties.name | Sort-Object

Then I can go through each list and see what properties are missing or extra.

#find extra  properties in the difference object
foreach ($name in $diffProp) {
    if ($refProp -notcontains $name) {
      $name
    }
} #foreach

#find missing reference properties from the difference object
foreach ($name in $refProp) {
    if ($diffProp -notcontains $name) {
       $name
    }
} #foreach

Of course, I want to create a meaningful result so that Gladys can tell at a glance how things compare.

Compare-PropertyName

Here's the PowerShell function I wrote.

Function Compare-PropertyName {
    [cmdletbinding(DefaultParameterSetName="default")]
    [alias("cpn")]
    [Outputtype("PSPropertyNameDifference","string","boolean")]
    Param(
        [Parameter(Position = 0, Mandatory)]
        [ValidateNotNullOrEmpty()]
        [object]$Reference,
        [Parameter(Position = 1, Mandatory)]
        [ValidateNotNullOrEmpty()]
        [object]$Difference,
        [Parameter(HelpMessage = "Indicate if property names match with a simple True/False.",ParameterSetName="quiet")]
        [switch]$Quiet,
        [Parameter(HelpMessage = "Only show missing property names in the difference object.",ParameterSetName="missing")]
        [switch]$MissingOnly,
        [Parameter(HelpMessage = "Only show extra property names in the difference object.",ParameterSetName="extra")]
        [switch]$ExtraOnly
    )

    #get property names from the first item
    $refProp = $Reference[0].psobject.properties.name | Sort-Object
    Write-Verbose "Found $(($refProp).count) reference properties"

    $diffProp = $Difference[0].psobject.properties.name | Sort-Object
    Write-Verbose "Found $(($diffProp). count) difference properties"

    $missing = [System.Collections.Generic.List[string]]::new()
    $extra = [System.Collections.Generic.List[string]]::new()

    #find extra  properties in the difference object
    foreach ($name in $diffProp) {
        if ($refProp -notcontains $name) {
            Write-Verbose "$Name not found in the reference object"
            $extra.Add( $name)
        }
    } #foreach

    #find missing reference properties from the difference object
    foreach ($name in $refProp) {
        if ($diffProp -notcontains $name) {
            Write-Verbose "$Name not found in the difference object"
            $missing.Add( $name)
        }
    } #foreach

    #create a custom object for the reseult
    $result = [pscustomobject]@{
            PSTypename = "PSPropertyNameDifference"
            Missing = $missing
            Extra = $Extra
            ReferenceProperties = $refProp
            DifferenceProperties = $diffProp
            ReferenceCount = $refProp.count
            DifferenceCount = $diffProp.count
         }

         switch ($pscmdlet.ParameterSetName) {
            "Quiet" {
                if ($result.missing.count -eq 0 -AND $result.extra.count -eq 0) {
                    $true
                }
                else {
                    $false
                }
            }
            "Missing" {
                $result.Missing
            }
            "Extra" {
                $result.Extra
            }
            Default {
                $result
            }
         } #switch


} #close function

The function generates one type of object by default.

But I also wanted to offer alternatives that would give Gladys information she might want quickly.

The results are all relative to the difference object. The difference object, $b, is missing the property "tabitha", for example.

I'd love to hear how this works for you. I think this will make a good addition to the PSScriptTools module so look for an update in the next few weeks.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

8 thoughts on “Comparing PowerShell Property Names”

  1. Matt Gray says:
    February 24, 2021 at 1:10 pm

    compare-object $a[0].psobject.properties.name $b[0].psobject.properties.name

    1. Jeffery Hicks says:
      February 24, 2021 at 5:01 pm

      Sure. That will work. My initial ideas included this very technique. But I’m not a big fan of how Compare-Object works and I don’t think the output is very automation friendly. I also wanted to simplify the process, or abstract it, as much as possible. That’s where functions come in.

  2. Tsvika Nahir says:
    February 25, 2021 at 4:30 pm

    This is very helpful, thanks. But a bigger comparing challenge is with deep objects. Say a car object has three properties: Make and Model, both strings, and an Engine that is an object by itself with a NumOfCylinder property.

    What is the best way to deep compare two cars? Ideally without knowing in advance the type structure and the properties names.

    1. Jeffery Hicks says:
      February 25, 2021 at 5:07 pm

      Comparing objects is harder, especially as they get more complex. I find Compare-Object ok with simple comparisons, although I don’t care for the output. I’m thinking of some ways to write my own object comparison command.

    2. Jeffery Hicks says:
      February 25, 2021 at 5:08 pm

      I also think that with any type of comparison, you have to know something about the object. That’s where Get-Member comes into the picture.

  3. Craig Osborn says:
    March 9, 2021 at 12:14 pm

    Hi Jeff, this is really helpful thanks. I had no idea that all objects had PSObject. This has greatly simplified the code I need for a task as I can dynamically create XML elements required based on the object being passed through the pipeline. I was using the Get-Member way of doing things, but that seems to present the property names in alphabetical order, which is not what I wanted. Your shared knowledge here solves that, thank you!

  4. Solino de Baay says:
    April 23, 2021 at 11:11 am

    Changed the missing and extra variables into arrays on PowerShell 5.1:
    $missing = @()
    $extra = @()
    And in stead of Add()-ing to them I just used +=.
    The [System.Collections.Generic.List[string]]::new() were not found in my version of PowerShell but got it to work with the above changes.

    Hope that helps others.

    1. Jeffery Hicks says:
      April 23, 2021 at 12:02 pm

      I used [System.Collections.Generic.List[string]] because technically it performs better than using arrays which must be recreated every time you change them. It is interesting that even though you are running PowerShell 5.1 you don’t have this .NET class. You must be running an older version of Windows or the .NET Framework.

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d