Union and Intersection in PowerShell?
I have an array of objects of the following structure:
structure Disk
{
int UID;
String Computer;
}
A computer may have a bunch of shared disks, and a disk may be shared among computers.
I want to find out all the disks common to all the computers. For example, I have computer A, B, and C; Disks 1, 2, and 3. The disk array is {1,A}, {1,B}, {2,A},{2,B},{2,C},{3,A}. The result that I want should be the disk 2, because it appears on A, B, and C.
Is there a effective way to achieve this?
With multiple foreach loops it's achievable, but definitely I want a better way. I'm thinking about operations like intersection, but didn't find this in PowerShell.
Assuming $arr
is the array, you can do like this:
$computers = $arr | select -expand computer -unique
$arr | group uid | ?{$_.count -eq $computers.count} | select name
In general, I would approach union and intersection in Powershell like this:
$a = (1,2,3,4)
$b = (1,3,4,5)
$a + $b | select -uniq #union
$a | ?{$b -contains $_} #intersection
But for what you are asking, the above solution works well and not really about union and intersection in the standard definition of the terms.
Update:
I have written pslinq which provides Union-List
and Intersect-List
that help to achieve set union and intersection with Powershell.
You can also do
$a = (1,2,3,4)
$b = (1,3,4,5)
Compare-Object $a $b -PassThru -IncludeEqual # union
Compare-Object $a $b -PassThru -IncludeEqual -ExcludeDifferent # intersection
Doesn't work if $a
is null though.
For set subtraction (a - b):
$a | ?{-not ($b -contains $_)}
While this won't work in the earliest versions, in more recent versions you can just call the .NET LINQ extension functions directly, e.g.
[system.linq.enumerable]::union([object[]](1,2,3),[object[]](2,3,4))
(Without the cast to some enumerable type, PowerShell throws a "cannot find overload" error.)
This definitely works in PowerShell V4 and V5 and definitely doesn't in V2. I don't have a system at hand with V3.