Kellen Stuart
Kellen Stuart

Reputation: 8933

PowerShell - How to tell if two objects are identical

Let's say you have two objects that are identical (meaning they have the same properties and the same values respectively).

How do you test for equality?

Example

$obj1 & $obj2 are identical

enter image description here

Here's what I've tried:

if($obj1 -eq $obj2)
{
    echo 'true'
} else {
    echo 'false'
}
# RETURNS "false"

if(Compare-Object -ReferenceObject $obj1 -DifferenceObject $obj2)
{
    echo 'true'
} else {
    echo 'false'
}
# RETURNS "false"

Edit

This is not identical

enter image description here

Upvotes: 1

Views: 17734

Answers (5)

Bill_Stewart
Bill_Stewart

Reputation: 24575

You can compare two PSObject objects for equality of properties and values by using Compare-Object to compare the Properties properties of both PSObjectobjects. Example:

if ( -not (Compare-Object $obj1.PSObject.Properties $obj2.PSObject.Properties) ) {
  "object properties and values match"
}
else {
  "object properties and values do not match"
}

If you want it in a function:

function Test-PSCustomObjectEquality {
  param(
    [Parameter(Mandatory = $true)]
    [PSCustomObject] $firstObject,

    [Parameter(Mandatory = $true)]
    [PSCustomObject] $secondObject
  )
  -not (Compare-Object $firstObject.PSObject.Properties $secondObject.PSObject.Properties)
}

Upvotes: 6

KyleMit
KyleMit

Reputation: 30107

If you'd like to test for equality for every object property, one at a time, in order to compare and contrast two objects and see which individual pieces are different, you can use the following function, adapted from this article on how to compare all properties of two objects in Windows PowerShell

Function Compare-ObjectProperties {
    Param(
        [PSObject]$leftObj,
        [PSObject]$rightObj 
    )

    $leftProps = $leftObj.PSObject.Properties.Name
    $rightProps = $rightObj.PSObject.Properties.Name
    $allProps = $leftProps + $rightProps | Sort | Select -Unique

    $props = @()

    foreach ($propName in $allProps) {

        # test if has prop
        $leftHasProp = $propName -in $leftProps
        $rightHasProp = $propName -in $rightProps

        # get value from object
        $leftVal = $leftObj.$propName
        $rightVal = $rightObj.$propName

        # create custom output - 
        $prop = [pscustomobject] @{   
            Match = $(If ($propName -eq "SamAccountName" ) {"1st"} Else {
                        $(If ($leftHasProp -and !$rightHasProp ) {"Left"} Else {
                            $(If ($rightHasProp -and !$leftHasProp ) {"Right"} Else {
                                $(If ($leftVal -eq $rightVal ) {"Same"} Else {"Diff"})
                            })
                          })
                     })
            PropName = $propName
            LeftVal = $leftVal
            RightVal = $rightVal
        }

        $props += $prop
    }

    # sort & format table widths
    $props | Sort-Object Match, PropName | Format-Table -Property `
               @{ Expression={$_.Match}; Label="Match"; Width=6}, 
               @{ Expression={$_.PropName}; Label="Property Name"; Width=25}, 
               @{ Expression={$_.LeftVal }; Label="Left Value";    Width=40}, 
               @{ Expression={$_.RightVal}; Label="Right Value";   Width=40}

}

And then use like this:

$adUser1 = Get-ADUser 'Grace.Hopper' -Properties *
$adUser2 = Get-ADUser 'Katherine.Johnson' -Properties *   
Compare-ObjectProperties $adUser1 $adUser2

Couple Interesting Notes:

Upvotes: 1

Bacon Bits
Bacon Bits

Reputation: 32180

Here's the function I used:

function Test-ObjectEquality {
    param(
        [Parameter(Mandatory = $true)]
        $Object1,
        [Parameter(Mandatory = $true)]
        $Object2
    )

    return !(Compare-Object $Object1.PSObject.Properties $Object2.PSObject.Properties)
}

Examples:

PS C:\> $obj1 = [pscustomobject] @{ 'a' = '5'; 'b' = 7; };
PS C:\> $obj2 = [pscustomobject] @{ 'a' = '5'; 'b' = 7; };
PS C:\> Test-ObjectEquality $obj1 $obj2
True
PS C:\> $obj2 = [psobject] @{ 'a' = '5'; 'b' = 7; };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
PS C:\> $obj2 = New-Object -TypeName PSObject -Property @{ 'a' = '5'; 'b' = 7; };
PS C:\> Test-ObjectEquality $obj1 $obj2
True
PS C:\> $obj2 = [pscustomobject] @{ 'c' = '6'; 'b' = 7; };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
PS C:\> $obj2 = [pscustomobject] @{ 'a' = '5'; 'b' = 8; };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
PS C:\> $obj2 = [pscustomobject] @{ 'a' = '5'; 'b' = 7; c = 8 };
PS C:\> Test-ObjectEquality $obj1 $obj2
False
PS C:\> $obj2 = [pscustomobject] @{ 'a' = '5'; 'b' = '7'; };
PS C:\> Test-ObjectEquality $obj1 $obj2
False

I certainly believe it's possible for this to miss things; however, if you look at what's in Properties you can see what's being compared for every property on an object:

PS C:\> $obj1.PSObject.Properties | Select-Object -First 1


MemberType      : NoteProperty
IsSettable      : True
IsGettable      : True
Value           : 5
TypeNameOfValue : System.String
Name            : a
IsInstance      : True

It's not often that I've cared about more than the MemberType, Name, TypeNameOfValue, or Value of an object's properties.

Also, note that if you really need to, you can compare .PSObject.Members instead of .PSObject.Properties. That will compare properties and methods, although you're only comparing the method calls and not the method definitions.

Upvotes: 0

Kellen Stuart
Kellen Stuart

Reputation: 8933

I wrote a function that checks for exact equality:

 function Global:Test-IdenticalObjects
 {
    param(
        [Parameter(Mandatory=$true)]$Object1,
        [Parameter(Mandatory=$true)]$Object2,
        $SecondRun=$false
    )

    if(-not ($Object1 -is [PsCustomObject] -and $Object2 -is [PsCustomObject))
    {
        Write-Error "Objects must be PsCustomObjects"
        return
    }

    foreach($property1 in $Object1.PsObject.Properties)
    {
        $prop1_name = $property1.Name
        $prop1_value = $Object1.$prop1_name
        $found_property = $false
        foreach($property2 in $Object2.PsObject.Properties)
        {
            $prop2_name = $property2.Name
            $prop2_value = $Object2.$prop2_name
            if($prop1_name -eq $prop2_name)
            {
                $found_property = $true
                if($prop1_value -ne $prop2_value)
                {
                    return $false
                }
            }
        } # j loop
        if(-not $found_property) { return $false }
    } # i loop
    if($SecondRun)
    {
        return $true
    } else {
        Test-IdenticalObjects -Object1 $Object2 -Object2 $Object1 -SecondRun $true
    }
 } # function

Upvotes: -1

Maximilian Burszley
Maximilian Burszley

Reputation: 19684

I'd suggest using Compare-Object for this task:

Function Test-Objects
{
    Param(
    [Parameter(Mandatory,Position=0)]
    [PSCustomObject]$Obj1,
    [Parameter(Mandatory,Position=1)]
    [PSCustomObject]$Obj2
    )

    [Void](Compare-Object -ReferenceObject $Obj1.PSObject.Properties -DifferenceObject.PSObject.Properties $Obj2 -OutVariable 'Test')

    ## Tests whether they are equal, no return = success
    If (-not $Test)
    {
        $True
    }
    Else
    {
        $False
    }
}

PS C:\> $Obj1 = [PSCustomObject]@{
    Property1 = 'Value1'
    Property2 = 'Value2'
    Property3 = 'Value3'
    Property4 = 'Value4'
    Property5 = 'Value5'
}
PS C:\> $Obj2 = [PSCustomObject]@{
    Property1 = 'Value1'
    Property2 = 'Value2'
    Property3 = 'Value3'
    Property4 = 'Value4'
    Property5 = 'Value5'
}
PS C:\> Test-Objects $Obj1 $Obj2
True
PS C:\> $Obj2 | Add-Member -MemberType 'NoteProperty' -Name 'Prop6' -Value 'Value6'
PS C:\> Test-Objects $Obj1 $Obj2
False

Upvotes: 2

Related Questions