Using some PowerShell kung-fu, you can easily do Powershell compare arrays of all kinds of objects. There are many different scenarios you may find yourself in so let’s dig in and see the ways we can build PowerShell to compare arrays.
Not a reader? Watch this related video tutorial!To determine the best way to compare arrays, you must first figure out what types of elements are in both arrays.
- Do both arrays contains all of the same type of object?
- Do both arrays have the same number of elements?
- Are there different types of objects in each array?
You must know the answer to each of these questions before you can accurate compare arrays. Let’s cover each scenario.
Comparing Arrays of Strings
One of the easiest ways to compare arrays with PowerShell is if you have two arrays only containing strings. When you find yourself in this position, you’ve got a few different ways to compare strings in the arrays.
Using the -Contains or -In Operators
The -contains
operator is a PowerShell operator that allows you to check to see if an object is in a collection. The -contains
operator natively doesn’t understand collections but y0u can build code to make it do your bidding.
Let’s say a collection (array) contains four strings like below.
$array = @('blue','red','purple','pink')
The -contains
operator works by checking if a single string is in that array like this:
$array -contains 'pink'
When the collection on the left contains that string, PowerShell will return True. If not, it will return False.
We can compare arrays using the -contains
operator by reading each string in an array and checking to see if the other array contains that string.
Let’s say I want to compare two arrays to see which strings in the first array exist in the second array.
$array = @('blue','red','purple','pink')
$array2 = @('brown','red','black','yellow')
$array | ForEach-Object {
if ($array2 -contains $_) {
Write-Host "`$array2 contains the `$array1 string [$_]"
}
}
You could alternatively use the -in
operator which is identical to the -contains
operator yet the syntax is opposite. When using the -contains
operator, the array is defined on the left side. Using the -in
operator, the array is defined on the right side like below:
$array | ForEach-Object {
if ($_ -in $array2) {
Write-Host "`$array2 contains the `$array1 string [$_]"
}
}
Using Where-Object
Alternatively, you could also use the Where-Object
cmdlet to return all strings in one array in the other like below.
$array | Where-Object -FilterScript { $_ -in $array2 }
Using the Compare-Object Cmdlet
You can also use PowerShell to compare arrays using the Compare-Object
cmdlet. This cmdlet takes a reference object and a difference object and returns a side indicator indicating which elements are and are not in either array.
Compare-Object -ReferenceObject $array -DifferenceObject $array2
You can see below that the Compare-Object
cmdlet allows you to compare both arrays at once. If the SideIndicator
property is =>
, this means the InputObject
property returned is in the DifferenceObject
value and not in the ReferenceObject
value and vice versa for the <=
SideIndicator
.
By default, Compare-Object
returns differences. You can also return all of the strings in each array that are in both by using the IncludeEqual
parameter.
Comparings Arrays of Complex Objects
Simple enough, right? Now, let’s bring objects into the mix. Let’s say we’ve got a field in this HR database of ours and we’d like to populate this into the Active Directory description field. Prior to doing this, we first have to have a common identifier. In my environment, there’s an employee number both in the HR database and in the custom Active Directory attribute. So let’s attempt to match this.
First, let’s see what happens if we try our previous approach. Here’s the CSV file we’re using.
Here’s how I get our two datasets.
$ad_users = Get-AdUser -Filter {enabled -eq $true} -Properties employeeNumber | select employeenumber,samaccountname,description
$users_from_database = Import-Csv 'database_users.csv' | select employee number
What happens when we run these two arrays through the same scenario as our strings? Absolutely nothing. Why?
The reason is because you can’t typically say $object1 -eq $object2
because objects are more complex than a simple string, boolean or integer. There are some other circumstances where this isn’t the case but I try to make it a habit to compare object properties; not entire objects. So in this instance, we have to do something like this:
$ad_user[0].employeeNumber -eq $users_from_database[0].employeeNumber
What’s the solution? Currently, I’ve got two solutions. When dealing with thousands of objects it’s not quick but it works. I’d like to know if anyone else has any other suggestions.
$ad_employee_numbers = $ad_users | % {$_.employeenumber}
## Create an array of strings of only the AD user's employee number
$users_from_database | Where-Object -FilterScript { $ad_employee_numbers -contains $_.employeeNumber }
We can also use Compare-Object
although this is slower.
$ad_employee_numbers = $ad_users | ForEach-Object {$_.employeenumber}
## Create an array of strings of only the AD user's employee number
$database_user_employee_numbers = $users_from_database | ForEach-Object {$_.employeenumber}
## Create an array of strings of only the database user's employee number
(Compare-Object $ad_employee_numbers $database_user_employee_numbers -IncludeEqual | Where-Object -FilterScript {$_.SideIndicator -eq '=='}).InputObject
Conclusion
There are many different ways to use PowerShell to compare arrays. Arrays can be complex and thoroughly understanding objects inside of the arrays will greatly help you compare them.