Finding common elements in two PowerShell arrays
I know there are a few questions on this already, but I've tried many solutions and keep getting the same error. The problem is this:
I have 2 arrays (each index represents a folder)
$originDirsArr = @(2, 257, 256, 3, 4, 10)
$tempDirArr = @(2, 257, 256, 3, 4)
I want to compare $ arr2 again with $ arr1 and if there is something in $ arr1 not in $ arr2, then Delete. Those. in this situation 10 DNEs in $ arr2, so the folder must be deleted.
Here's what I've tried:
$c = Compare-Object -ReferenceObject ($originDirsArr) `
-DifferenceObject ($tempDirArr) -Passthru
$c
Also:
while ($t -lt $originDirsArr.length){
$originDirsArr[$t]
if ( $tempDirArr -notContain $originDirsArr[$t]){
"$_ does not exist in original and needs to be deleted"
}else{
"$_ does still exist in the temp"
}
$t++
}
Finally:
Compare-Object $originDirsArr $tempDirArr | ForEach-Object { $_.InputObject }
Every time I get some error, either is ReferenceObject or DifferenceObject is null. I know it is not null because I can print the content and even index by t in this example, I still have the content.
Thanks in advanced, any help would be greatly appreciated!
source to share
I also have an aversion to compare-object
. Since these are simple arrays, one trick foreach
and -notcontains
will do the trick.
$originDirsArr = @(2, 257, 256, 3, 4, 10)
$tempDirArr = @(2, 257, 256, 3, 4)
foreach ($item in $originDirsArr) {
if ($tempDirArr -notcontains $item) {
Write-Output "Do something with $item";
}
}
source to share
Use a shared collection HashSet . It requires .NET 3.5. It has an IntersectWith method that will modify a set that only includes items in both collections (for example, AND
with two collections):
$originDirsSet = New-Object 'Collections.Generic.HashSet[int]' ,@(2, 257, 256, 3, 4, 10)
$tempDirSet = @(2, 257, 256, 3, 4)
$originDirsSet.IntersectWith( $tempDirSet )
# $originalDirsSet now contains 2, 257, 256, 3, 4
It has other set based methods:
-
ExceptWith
: removes all elements in the specified collection from the current objectHashSet<T>
. -
SymmetricExceptWith
: Modifies the current objectHashSet<T>
, containing only those elements that are present either in this object or in the specified collection, but not both (i.e. it looks like aXOR
collection). -
UnionWith
: Modifies the current objectHashSet<T>
, containing all the items present in it, the specified collection, or both (that is, it looks like aOR
collection).
source to share
Here is 1 liner for that. This is the same as the accepted answer in that it iterates through one array then uses it -contains
to compare that element with another array. The difference here is that we are using a function where-object
and returning a result set instead of having an if statement in a for loop. Also, the use of PowerShell shorthand to reduce the amount of code (good or bad smart is controversial).
#setting up
$originDirsArr = @(2, 257, 256, 3, 4, 10)
$tempDirArr = @(2, 257, 256, 229, 3, 4)
#get items in 1 array but not the other
[int[]]$leftOnly = $originDirsArr | ?{$tempDirArr -notcontains $_}
#or an intersect operation
[int[]]$intersect = $originDirsArr | ?{$tempDirArr -contains $_}
#display results
"Left Only"; $leftOnly
"Intersect"; $intersect
Or, if you do this a lot, it might be helpful to have a handy function for this type of operation:
set-alias ?@@ Apply-ArrayOperation #http://stackoverflow.com/a/29758367/361842
function Apply-ArrayOperation {
[CmdletBinding()]
param(
[Parameter(Mandatory, Position=0)]
[object[]]$Left
,
[Parameter(Mandatory,ParameterSetName='exclude', Position=1)]
[switch]$exclude
,
[Parameter(Mandatory,ParameterSetName='intersect', Position=1)]
[switch]$intersect
,
[Parameter(Mandatory,ParameterSetName='outersect', Position=1)] #not sure what the correct term for this is
[switch]$outersect
,
[Parameter(Mandatory,ParameterSetName='union', Position=1)] #not sure what the correct term for this is
[switch]$union
,
[Parameter(Mandatory,ParameterSetName='unionAll', Position=1)] #not sure what the correct term for this is
[switch]$unionAll
,
[Parameter(Mandatory, Position=2)]
[object[]]$Right
)
begin {
#doing this way so we can use a switch staement below, whilst having [switch] syntax for the function caller
[int]$action = 1*$exclude.IsPresent + 2*$intersect.IsPresent + 3*$outersect.IsPresent + 4*$union.IsPresent + 5*$unionAll.IsPresent
}
process {
switch($action) {
1 {$Left | ?{$Right -notcontains $_}}
2 {$Left | ?{$Right -contains $_} }
3 {@($Left | ?{$Right -notcontains $_}) + @($Right | ?{$Left -notcontains $_})}
4 {@($Left) + @($Right) | select -Unique}
5 {@($Left) + @($Right)}
}
}
}
$array1 = @(1,3,5,7,9)
$array2 = @(2,3,4,5,6,7)
"Array 1"; $array1
"Array 1"; $array2
"Array 1 Exclude Array 2"; ?@@ $array1 -exclude $array2
"Array 1 Intersect Array 2"; ?@@ $array1 -intersect $array2
"Array 1 Outersect Array 2"; ?@@ $array1 -outersect $array2
"Array 1 Union Array 2"; ?@@ $array1 -union $array2
"Array 1 UnionAll Array 2"; ?@@ $array1 -unionall $array2
source to share