# Comparing two multi-dimensional arrays

**URL:** <https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455>\
**Category:** PowerShell Help\
**Created:** [July 1, 2015, 5:50am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455 "2015-07-01T05:50:15Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![dan-trimpey](https://avatars.discourse-cdn.com/v4/letter/d/54ee81/32.png) [@dan-trimpey](https://forums.powershell.org/u/dan-trimpey)\
**Post date:** [July 1, 2015, 5:50am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/1 "2015-07-01T05:50:15Z")

</div>

I’m trying to come up with the most efficient way to compare two arrays where there could be any number of columns in each array, but we are matching on a specified column from each array. I want to retain all data from each array. Some arrays could contain duplicates. I am finding that when I compare arrays with more than a few thousand lines, the compare can start to take a long time, over an hour in some cases.

Is my Compare Function the most efficient way to do this?

Here is my sample code:

Function RJ-CombinedCompare() {  
[CmdletBinding()]  
PARAM([Parameter(Mandatory=$True)]$List1,$L1Match,$List2,$L2Match)  
$List = $List1 | %{[PSCustomObject]@{L1Data = $_; L2Data = ‘NA’}}  
$List += $List2 | %{[PSCustomObject]@{L1Data = ‘NA’; L2Data = $_}}  
foreach ($Object in $List.L1Data.$L1Match + $List.L2Data.$L2Match | select -Unique) {  
$Match1 = @()  
$Match2 = @()  
foreach ($Object1 in $List.L1Data -ne ‘NA’) {  
if ($Object1.$L1Match -eq $Object) {$Match1 += $Object1}  
}  
foreach ($Object2 in $List.L2Data -ne ‘NA’) {  
if ($Object2.$L2Match -eq $Object) {$Match2 += $Object2}  
}  
[PSCustomObject]@{MatchValue = $Object; L1Matches = $Match1.count; L2Matches = $Match2.count; List1 = $Match1; List2 = $Match2}  
}  
}

$List1 = @(  
[PSCustomObject]@{Alias = 1; Place = 1; Extra = ‘c’}  
[PSCustomObject]@{Alias = 2; Place = 3; Extra = ‘a’}  
[PSCustomObject]@{Alias = 3; Place = 2; Extra = ‘c’}  
[PSCustomObject]@{Alias = 4; Place = 1; Extra = ‘a’}  
[PSCustomObject]@{Alias = 22; Place = 3; Extra = ‘g’}  
[PSCustomObject]@{Alias = 2; Place = 3; Extra = ‘a’}  
[PSCustomObject]@{Alias = 5; Place = 6; Extra = ‘e’}  
[PSCustomObject]@{Alias = 4; Place = 2; Extra = ‘c’}  
[PSCustomObject]@{Alias = 1; Place = 6; Extra = ‘b’}  
)

$List2 = @(  
[PSCustomObject]@{Name = 1; Place = 5; Somthing = ‘a1’}  
[PSCustomObject]@{Name = 1; Place = 1; Somthing = ‘b6’}  
[PSCustomObject]@{Name = 5; Place = 1; Somthing = ‘c3’}  
[PSCustomObject]@{Name = 2; Place = 4; Somthing = ‘a3’}  
[PSCustomObject]@{Name = 12; Place = 6; Somthing = ‘a1’}  
[PSCustomObject]@{Name = 1; Place = 2; Somthing = ‘b1’}  
[PSCustomObject]@{Name = 2; Place = 7; Somthing = ‘d4’}  
[PSCustomObject]@{Name = 44; Place = 2; Somthing = ‘a5’}  
)

$Result = RJ-CombinedCompare -List1 $List1 -L1Match Alias -List2 $List2 -L2Match Name

---

<div class="post-metadata">

**Author:** ![rob-simmers](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/rob-simmers/32/1010_2.png) [@rob-simmers](https://forums.powershell.org/u/rob-simmers)\
**Post date:** [July 1, 2015, 6:37am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/2 "2015-07-01T06:37:53Z")

</div>

Have you looked at Compare-Object?

```
$List1 = @(
 [PSCustomObject]@{Alias = 1; Place = 1; Extra = 'c'}
 [PSCustomObject]@{Alias = 2; Place = 3; Extra = 'a'}
 [PSCustomObject]@{Alias = 3; Place = 2; Extra = 'c'}
 [PSCustomObject]@{Alias = 4; Place = 1; Extra = 'a'}
 [PSCustomObject]@{Alias = 22; Place = 3; Extra = 'g'}
 [PSCustomObject]@{Alias = 2; Place = 3; Extra = 'a'}
 [PSCustomObject]@{Alias = 5; Place = 6; Extra = 'e'}
 [PSCustomObject]@{Alias = 4; Place = 2; Extra = 'c'}
 [PSCustomObject]@{Alias = 1; Place = 6; Extra = 'b'}
 )
 
$List2 = @(
 [PSCustomObject]@{Name = 1; Place = 5; Somthing = 'a1'}
 [PSCustomObject]@{Name = 1; Place = 1; Somthing = 'b6'}
 [PSCustomObject]@{Name = 5; Place = 1; Somthing = 'c3'}
 [PSCustomObject]@{Name = 2; Place = 4; Somthing = 'a3'}
 [PSCustomObject]@{Name = 12; Place = 6; Somthing = 'a1'}
 [PSCustomObject]@{Name = 1; Place = 2; Somthing = 'b1'}
 [PSCustomObject]@{Name = 2; Place = 7; Somthing = 'd4'}
 [PSCustomObject]@{Name = 44; Place = 2; Somthing = 'a5'}
 )

 Compare-Object -ReferenceObject $List1 -DifferenceObject $List2 -Property Place
```

---

<div class="post-metadata">

**Author:** ![dan-trimpey](https://avatars.discourse-cdn.com/v4/letter/d/54ee81/32.png) [@dan-trimpey](https://forums.powershell.org/u/dan-trimpey)\
**Post date:** [July 1, 2015, 7:16am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/3 "2015-07-01T07:16:31Z")

</div>

Thanks for the response,

That works to some degree, but it doesn’t seem to maintain all the columns from each array. Can that be done with Compare-Object?

E.g:  
Compare-Object -ReferenceObject $List1 -DifferenceObject $List2 -Property Place -IncludeEqual

Place SideIndicator

* * *

1 ==  
1 ==  
2 ==  
6 ==  
2 ==  
5 =\>  
4 =\>  
7 =\>  
3 \<=  
3 \<=  
3 \<=  
6 \<=

With my Function, each Array's content is preserved in the result:  
RJ-CombinedCompare -List1 $List1 -L1Match Place -List2 $List2 -L2Match Place | ft

MatchValue L1Matches L2Matches List1 List2

* * *

1 2 2 {@{Alias=1; Place=1; … {@{Name=1; Place=1; S…  
3 3 0 {@{Alias=2; Place=3; … {}  
2 2 2 {@{Alias=3; Place=2; … {@{Name=1; Place=2; S…  
6 2 1 {@{Alias=5; Place=6; … {@{Name=12; Place=6; …  
5 0 1 {} {@{Name=1; Place=5; S…  
4 0 1 {} {@{Name=2; Place=4; S…  
7 0 1 {} {@{Name=2; Place=7; S…

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [July 1, 2015, 5:42pm UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/4 "2015-07-01T17:42:49Z")

</div>

Dan, your way absolutely ineffective, because you combine both arrays in beginning (for what reason? ) `$List = $List1 | %{[PSCustomObject]@{L1Data = $; L2Data = ‘NA’}}
$List += $List2 | %{[PSCustomObject]@{L1Data = ‘NA’; L2Data = $}}`

and later _never_ use it in combined form, but several times filter it for original arrays:  
`$Object in $List.L1Data.$L1Match +$List.L2Data.$L2Match `  
`foreach ($Object1 in $List.L1Data -ne ‘NA’) {`  
`if ($Object1.$L1Match -eq $Object) {$Match1 += $Object1}`  
and so on.

The code below 3 times more effective even on your sample. and should be more effective on large arrays. Btw, tell me how much…

```
#Require -Version 4.0
Function RJ-CombinedCompare() {
 [CmdletBinding()]
 PARAM(
	#Every parameter must be mandatory
	[Parameter(Mandatory=$True)]$List1,
	[Parameter(Mandatory=$True)]$L1Match,
	[Parameter(Mandatory=$True)]$List2,
	[Parameter(Mandatory=$True)]$L2Match)
 #Fill HASH with arrays of data from both arrays, hash keys is value to compare
 $hash = @{}
 foreach ($data in $List1) {
    $hash[$data.$L1Match] += ,$data
 }
 foreach ($data in $List2) {
    $hash[$data.$L2Match] += ,$data
 }
 # filter every hash value by existance of $L1Match field in data.
 # {$_.$L1Match} - subject to change if $L1Match property exists in both $List1 and $List2
 # or may be $null
 foreach ($kv in $hash.GetEnumerator()) {
	$m1, $m2 = $kv.Value.where( {$_.$L1Match}, 'Split')
    [PSCustomObject]@{
		MatchValue = $kv.Key
		L1Matches = $m1.Count
		L2Matches = $m2.Count
		List1 = $m1
		List2 = $m2
	}
 }
}

$List1 = @(
 [PSCustomObject]@{Alias = 1; Place = 1; Extra = 'c'}
 [PSCustomObject]@{Alias = 2; Place = 3; Extra = 'a'}
 [PSCustomObject]@{Alias = 3; Place = 2; Extra = 'c'}
 [PSCustomObject]@{Alias = 4; Place = 1; Extra = 'a'}
 [PSCustomObject]@{Alias = 22; Place = 3; Extra = 'g'}
 [PSCustomObject]@{Alias = 2; Place = 3; Extra = 'a'}
 [PSCustomObject]@{Alias = 5; Place = 6; Extra = 'e'}
 [PSCustomObject]@{Alias = 4; Place = 2; Extra = 'c'}
 [PSCustomObject]@{Alias = 1; Place = 6; Extra = 'b'}
 )

$List2 = @(
 [PSCustomObject]@{Name = 1; Place = 5; Somthing = 'a1'}
 [PSCustomObject]@{Name = 1; Place = 1; Somthing = 'b6'}
 [PSCustomObject]@{Name = 5; Place = 1; Somthing = 'c3'}
 [PSCustomObject]@{Name = 2; Place = 4; Somthing = 'a3'}
 [PSCustomObject]@{Name = 12; Place = 6; Somthing = 'a1'}
 [PSCustomObject]@{Name = 1; Place = 2; Somthing = 'b1'}
 [PSCustomObject]@{Name = 2; Place = 7; Somthing = 'd4'}
 [PSCustomObject]@{Name = 44; Place = 2; Somthing = 'a5'}
 )

RJ-CombinedCompare -List1 $List1 -L1Match Alias -List2 $List2 -L2Match Name
#Speed measurement
measure-command { 1..10000 | %{ $Result = RJ-CombinedCompare -List1 $List1 -L1Match Alias -List2 $List2 -L2Match Name } }
```

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [July 1, 2015, 5:50pm UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/5 "2015-07-01T17:50:27Z")

</div>

should warn You that in my code List1 and List2 in result objects is not arrays but collections. But may be You do not even notice the difference 🙂

---

<div class="post-metadata">

**Author:** ![dan-trimpey](https://avatars.discourse-cdn.com/v4/letter/d/54ee81/32.png) [@dan-trimpey](https://forums.powershell.org/u/dan-trimpey)\
**Post date:** [July 2, 2015, 1:39am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/6 "2015-07-02T01:39:21Z")

</div>

Hi Max,

Thanks so much for your assistance. In my tests, your Function is extremely fast. The only issue I am having is when I run the compare where I am Matching on columns that have the same name, in the result its combining the result into list 1. I’m trying to see how I can modify your Function to handle that case.

E.g.:

```
RJ-CombinedCompare -List1 $List1 -L1Match Place -List2 $List2 -L2Match Place | ft

                         MatchValue L1Matches L2Matches List1 List2                             
                         ---------- --------- --------- ----- -----                             
                                  7 1 0 {@{Name=2; Place=7; Somthing=d4}} {}                                
                                  6 3 0 {@{Alias=5; Place=6; Extra=e}, @... {}                                
                                  5 1 0 {@{Name=1; Place=5; Somthing=a1}} {}                                
                                  4 1 0 {@{Name=2; Place=4; Somthing=a3}} {}                                
                                  3 3 0 {@{Alias=2; Place=3; Extra=a}, @... {}                                
                                  2 4 0 {@{Alias=3; Place=2; Extra=c}, @... {}                                
                                  1 4 0 {@{Alias=1; Place=1; Extra=c}, @... {}
```

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [July 2, 2015, 6:39pm UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/7 "2015-07-02T18:39:12Z")

</div>

You can save data owner as

`$hash[$data.$L1Match] += ,[pscustomobject]@{Owner=‘l1’;Value=$data }`  
and for List2 too

use it here  
`$m1, $m2 = $kv.Value.where( {$_.Owner -eq ‘l1’}, ‘Split’)`

but then need to modify result assinging:  
`List1 = $m1.Value`

I do not test it, correct any erors if i make it 🙂

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [July 2, 2015, 6:45pm UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/8 "2015-07-02T18:45:18Z")

</div>

for owner better use [bool] or [int] identifier, not a [string], I use it only for information purposes

---

<div class="post-metadata">

**Author:** ![dan-trimpey](https://avatars.discourse-cdn.com/v4/letter/d/54ee81/32.png) [@dan-trimpey](https://forums.powershell.org/u/dan-trimpey)\
**Post date:** [July 7, 2015, 4:32am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/9 "2015-07-07T04:32:05Z")

</div>

Thanks for all you help Max.

Here’s the final Function. It’s a lot more than 3 times faster, my large compare went from 45min to 5 seconds!

```
Function RJ-CombinedCompare() {
    [CmdletBinding()]
    PARAM(
        #Every parameter must be mandatory
	    [Parameter(Mandatory=$True)]$List1,
	    [Parameter(Mandatory=$True)]$L1Match,
	    [Parameter(Mandatory=$True)]$List2,
	    [Parameter(Mandatory=$True)]$L2Match
    )
    #Fill HASH with arrays of data from both arrays, hash keys is value to compare
    $hash = @{}
    foreach ($data in $List1) {$hash[$data.$L1Match] += ,[pscustomobject]@{Owner='l1';Value=$($data)}}
    foreach ($data in $List2) {$hash[$data.$L2Match] += ,[pscustomobject]@{Owner='l2';Value=$($data)}}
    # filter every hash value by existance of $L1Match field in data.
    # {$_.$L1Match} - subject to change if $L1Match property exists in both $List1 and $List2
    # or may be $null
    foreach ($kv in $hash.GetEnumerator()) {
        $m1, $m2 = $kv.Value.where({$_.Owner -eq 'l1'}, 'Split')
        [PSCustomObject]@{
            MatchValue = $kv.Key
		    L1Matches = $m1.Count
		    L2Matches = $m2.Count
            L1MatchObject = $L1Match
            L2MatchObject = $L2Match
            List1 = $m1.Value
            List2 = $m2.Value
        }
    }
}
```

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [July 8, 2015, 8:30pm UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/10 "2015-07-08T20:30:17Z")

</div>

Congrats! 🙂

but change everywhere ‘l1’ and ‘l2’ to 1 and 2, it save you some more execution time and memory eliminating string allocation.

It’s already memory hungry.

---

<div class="post-metadata">

**Author:** ![dan-trimpey](https://avatars.discourse-cdn.com/v4/letter/d/54ee81/32.png) [@dan-trimpey](https://forums.powershell.org/u/dan-trimpey)\
**Post date:** [July 9, 2015, 7:19am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/11 "2015-07-09T07:19:04Z")

</div>

Thank you Sir! Its done:

```
Function RJ-CombinedCompare() {
    [CmdletBinding()]
    PARAM(
	    [Parameter(Mandatory=$True)]$List1,
	    [Parameter(Mandatory=$True)]$L1Match,
	    [Parameter(Mandatory=$True)]$List2,
	    [Parameter(Mandatory=$True)]$L2Match
    )
    $hash = @{}
    foreach ($data in $List1) {$hash[$data.$L1Match] += ,[pscustomobject]@{Owner='1';Value=$($data)}}
    foreach ($data in $List2) {$hash[$data.$L2Match] += ,[pscustomobject]@{Owner='2';Value=$($data)}}
    foreach ($kv in $hash.GetEnumerator()) {
        $m1, $m2 = $kv.Value.where({$_.Owner -eq '1'}, 'Split')
        [PSCustomObject]@{
            MatchValue = $kv.Key
            L1Matches = $m1.Count
            L2Matches = $m2.Count
            L1MatchObject = $L1Match
            L2MatchObject = $L2Match
            List1 = $m1.Value
            List2 = $m2.Value
        }
    }
}
```

---

<div class="post-metadata">

**Author:** ![pscookiemonster](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/pscookiemonster/32/634_2.png) [@pscookiemonster](https://forums.powershell.org/u/pscookiemonster)\
**Post date:** [July 9, 2015, 8:11am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/12 "2015-07-09T08:11:43Z")

</div>

Hello there!

A short while back, I borrowed some code from Lucio Silveira (MSFT) and Dave Wyatt (superstar) and duct taped together [a modified Join-Object](http://ramblingcookiemonster.github.io/Join-Object/).

That link walks through using it in a number of scenarios. It has quickly becomer one of the ‘most-used’ functions in my PowerShell toolbox.

Cheers!

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [July 9, 2015, 7:52pm UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/13 "2015-07-09T19:52:35Z")

</div>

Dan, be more watchful 🙂

you sould replace Owner=‘1’; to Owner=1; _Without quotes!_ to convert owner field from [string] to [int]  
and of course **$_.Owner -eq ‘1’_** _to **$** _ **.Owner -eq _1_**

and if you gonna more faster use arraylist, instead of array as in Join-Object.  
that modification get more time, but lit learn you well, if you need 🙂  
because each = ,obj assignment recreates new array

---

<div class="post-metadata">

**Author:** ![nickm](https://avatars.discourse-cdn.com/v4/letter/n/58f4c7/32.png) [@nickm](https://forums.powershell.org/u/nickm)\
**Post date:** [September 16, 2016, 2:36am UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/14 "2016-09-16T02:36:00Z")

</div>

The code works perfectly on v5. When running it on v3 I get the following exception:

```
Method invocation failed because [System.Management.Automation.PSCustomObject] doesn't contain a method named 'where'.
+ $m1, $m2 = $kv.Value.where({$_.Owner -eq 1}, 'Split')
```

How can I re-write the line

```
$m1, $m2 = $kv.Value.where({$_.List -eq 1}, 'Split')
```

to run it on v3 without changing the functionality? (Installing v5 ist not possible.)

---

<div class="post-metadata">

**Author:** ![dotnVo](https://avatars.discourse-cdn.com/v4/letter/d/4af34b/32.png) [@dotnVo](https://forums.powershell.org/u/dotnVo)\
**Post date:** [May 16, 2024, 8:40pm UTC](https://forums.powershell.org/t/comparing-two-multi-dimensional-arrays/4455/15 "2024-05-16T20:40:19Z")

</div>


