57

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user1076554
  • 591
  • 1
  • 4
  • 4
  • 1
    I would recommend to change the title to "Union, Intersection and Difference / Set Subtraction in PowerShell". So we'll have all set operations in one place. – Mehrdad Mirreza Apr 25 '16 at 09:15
  • 1
    Based on the question and suggestions here, I have created a PowerShell function to do Union, Intersection and Minus for objects. Check it out: https://sqljana.wordpress.com/2015/09/23/perform-set-operations-union-intersection-minus-complement-using-powershell/ – Jana Sattainathan Dec 02 '16 at 16:35

5 Answers5

103

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.

manojlds
  • 290,304
  • 63
  • 469
  • 417
26

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.

shamp00
  • 11,106
  • 4
  • 38
  • 81
15

For set subtraction (a - b):

$a | ?{-not ($b -contains $_)}

nerfologist
  • 761
  • 10
  • 23
10

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
unbob
  • 331
  • 3
  • 7
1

I realised no-one answered your specific example of computers a, b, c, with disks numbered 1, 2, 3 attached. Code and output given for Intersection, Union and Set difference across the three sets

Code

$a = @(1, 2, 3)
$b = @(1, 2)
$c = @(2)

'Intersection $a ⋂ $b ⋂ $c'
$a | Where-Object {$_ -In $b} | Where-Object {$_ -In $c}

'Union $a ⋃ $b ⋃ $c'
$a + $b + $c | Select-Object -Unique

'Set difference $a - $b - $c (items in $a but not $b or $c)'
$a | Where-Object {$_ -NotIn $b} | Where-Object {$_ -NotIn $c}

Output

Intersection $a ⋂ $b ⋂ $c
2
Union $a ⋃ $b ⋃ $c
1
2
3
Set difference $a - $b - $c (items in $a but not $b or $c)
3
Jason S
  • 1,361
  • 20
  • 24