Glenn Slaven
Glenn Slaven

Reputation: 34223

In Powershell how can I check if all items from one array exist in a second array?

So let's say I have this array:

$requiredFruit= @("apple","pear","nectarine","grape")

And I'm given a second array called $fruitIHave. How can I check that $fruitIHave has everything in $requiredFruit. It doesn't matter if there are more items in $fruitIHave just as long as everything in $requiredFruit is there.

I know I could just iterate over the list, but that seems inefficient, is there a built-in method for doing this?

Upvotes: 18

Views: 42657

Answers (4)

Rynant
Rynant

Reputation: 24343

If you have the arrays:

$requiredFruit= @("apple","pear","nectarine","grape")
$someFruit= @("apple","banana","pear","nectarine","orange","grape")
$moreFruit= @("apple","banana","nectarine","grape")

You can get a boolean result with:

'Check $someFruit for $requiredFruit'
-not @($requiredFruit| where {$someFruit -notcontains $_}).Count

'Check $moreFruit for $requiredFruit'
-not @($requiredFruit| where {$moreFruit -notcontains $_}).Count

Using the count of an array protects against a single value not matching that evaluates as False. For example:

# Incorrect result
-not (0| where {(1,2) -notcontains $_})

# Correct result
-not @(0| where {(1,2) -notcontains $_}).Count

With PowerShell v3, you can use select -first 1 to stop the pipeline when the first mismatch is found (in v2 select -first 1 allows only one object through, but previous elements of the pipeline continue to process).

-not @($requiredFruit| where {$moreFruit -notcontains $_}| select -first 1).Count

Upvotes: 17

JPBlanc
JPBlanc

Reputation: 72680

Do you try Compare-Object :

$requiredFruit= @("apple","pear","nectarine","grape")
$HaveFruit= @("apple","pin","nectarine","grape")
Compare-Object $requiredFruit $haveFruit
InputObject                                                 SideIndicator
-----------                                                 -------------
pin                                                         =>
pear                                                        <=

Compare-Object $requiredFruit $haveFruit | where {$_.sideindicator -eq "<="} | % {$_.inputobject}
pear

Upvotes: 28

Lance U. Matthews
Lance U. Matthews

Reputation: 16612

One way or the other, you're going to have to iterate through one or both arrays. Here's a one-liner approach:

$hasAllRequiredFruit = ($requiredFruit | Where-Object { $fruitIHave -contains $_ }).Length -eq $requiredFruit.Length;

A foreach loop would be better because you can stop iterating as soon as you find a required fruit that is missing:

$hasAllRequiredFruit = $true;
foreach ($f in $requiredFruit)
{
    if ($fruitIHave -notcontains $f)
    {
       $hasAllRequiredFruit = $false;

       break;
    }
}

Upvotes: 1

mjolinor
mjolinor

Reputation: 68341

Not exactly "builtin" but:

[regex] $RF_regex = ‘(?i)^(‘ + (($requiredFruit |foreach {[regex]::escape($_)}) –join “|”) + ‘)$’

($fruitIHave -match $RF_regex).count -eq $requiredFruit.count

That creates an alternating regex from the elements of $requiredFruit. Matched against $fruitIHave, it will return all the items that matched. If $fruitIhave could potentially have duplicates of the same fruit you may need to run that match result through get-unique before you do the count. It may be slower than iterating over the list for a single comparison, but once you have the regex built it will do repetitive matches very efficiently.

Upvotes: 1

Related Questions