6

I have two arrays in Powershell. Each Array contains an array of objects. These objects have two properties:

Name: String
Id: GUID

The first Array has 4413 objects in it and the second has 4405. The counts are irrelevant, but I only mention them to note that the contents of Array1 and Array2 are different.

Here is my current code (pseudo):

#Fill Array1
$Array1 = Fill-Array1

#Fill Array2
$Array2 = Fill-Array2

#Loop through the arrays and write out the names of all items in Array2 that are different than Array1
ForEach($Val in $Array2)
{
    $Name = $Val.Name

    If($Array1 -notcontains $Val) //this does not work
    {
        Write-Host $Name
    }
}

What is the correct way to check for the existence of the object in Array1? Is my only option to do a nested loop?

Update, using the answer from Manu P below, the following is how I implemented the solution:

#Fill Array1
    $Array1 = Fill-Array1
    
    #Fill Array2
    $Array2 = Fill-Array2
    
    #Compare the arrays
    $ComparedResults = Compare-Object -ReferenceObject $Array1 -DifferenceObject $Array2 #I left out the -IncludeEqual param because I don't care about those results

    ForEach($Result in $ComparedResults)
    {
        If($Result.SideIndicator -eq "=>") #the value in Array2 is different than Array1
        {
            $Val = $Result.InputObject

            Write-Host $Val.Name            
        }        
    }
karatedog
  • 2,508
  • 19
  • 29
crackedcornjimmy
  • 1,972
  • 5
  • 26
  • 42
  • 1
    When I do this exact same method using 2 arrays with just some random numbers in them, it works perfectly. Maybe it has something to do with the type of object in the arrays? – cet51 Aug 09 '17 at 16:59
  • 2
    @CoryEtmund google "reference equality vs value equality" :-) – Mathias R. Jessen Aug 09 '17 at 17:07

3 Answers3

8

You can use Compare-Object cmdlet :

Compare-Object -ReferenceObject $Array1 -DifferenceObject $Array2 -IncludeEqual

https://learn.microsoft.com/fr-fr/powershell/module/Microsoft.PowerShell.Utility/Compare-Object?view=powershell-5.0

https://technet.microsoft.com/fr-fr/library/ee156812.aspx

Community
  • 1
  • 1
Manu
  • 1,685
  • 11
  • 27
2

As GUIDs are comparable you can simply (inner)join the arrays using the function described here: https://stackoverflow.com/a/45483110/1701026

$Array1 = @(
    [pscustomobject]@{Name = "a"; Id = [guid]::NewGuid()}
    [pscustomobject]@{Name = "b"; Id = [guid]::NewGuid()}
    [pscustomobject]@{Name = "c"; Id = [guid]"de5e32c6-5338-4287-8c21-2dd5957cfffe"}
    [pscustomobject]@{Name = "d"; Id = [guid]"345f9ac2-86a0-4b01-b843-5f4bd55f5e21"}
    [pscustomobject]@{Name = "e"; Id = [guid]"953c1442-ed51-445e-a8f5-687120d98f83"}
    [pscustomobject]@{Name = "f"; Id = [guid]::NewGuid()}
)

$Array2 = @(
    [pscustomobject]@{Name = "b"; Id = [guid]::NewGuid()}
    [pscustomobject]@{Name = "c"; Id = [guid]"345f9ac2-86a0-4b01-b843-5f4bd55f5e21"}
    [pscustomobject]@{Name = "d"; Id = [guid]"345f9ac2-86a0-4b01-b843-5f4bd55f5e21"}
    [pscustomobject]@{Name = "e"; Id = [guid]"953c1442-ed51-445e-a8f5-687120d98f83"}
    [pscustomobject]@{Name = "f"; Id = [guid]"953c1442-ed51-445e-a8f5-687120d98f83"}
    [pscustomobject]@{Name = "g"; Id = [guid]::NewGuid()}
)

$Array1 | InnerJoin $Array2 -Using Name,Id | Format-Table

Result:

Id                                   Name
--                                   ----
345f9ac2-86a0-4b01-b843-5f4bd55f5e21 d
953c1442-ed51-445e-a8f5-687120d98f83 e

To get all records with the same name where Id is not equal:

$Array1 | InnerJoin $Array2 -on {$Left.Name -eq $Right.Name -and $Left.Id -ne $Right.Id} -Merge @{Name = {$Left.$_}} | Format-Table

Result:

Id                                                                           Name
--                                                                           ----
{3b175777-e88f-4248-965b-51dec8639fd6, a43d68de-9ed5-4a13-8f8e-61c6cc501458} b
{de5e32c6-5338-4287-8c21-2dd5957cfffe, 345f9ac2-86a0-4b01-b843-5f4bd55f5e21} c
{2a8c4bf7-cf3a-4c3d-b27b-ecb6f4a4fa43, 953c1442-ed51-445e-a8f5-687120d98f83} f
iRon
  • 20,463
  • 10
  • 53
  • 79
2

Continuing off from what Manu gave, this helped me a lot, I was interesting in something similar but matching on one property within an object, what I used was like this. This example adds unmatched to the end of an object. The last line of code strips off the SideIndicator that gets added in the process.

$remainder = Compare-Object -ReferenceObject $a -DifferenceObject $b -Property "mail" -PassThru | where-object SideIndicator -eq "<="
$b += $remainder
$b = $b | Select-Object -Property * -ExcludeProperty SideIndicator