27

How can I use the Array.Find method in powershell?

For example:

$a = 1,2,3,4,5
[Array]::Find($a, { args[0] -eq 3 })

gives

Cannot find an overload for "Find" and the argument count: "2".
At line:3 char:1
+ [Array]::Find($a, { $args[0] -eq 3 })
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

The array class has the methods I expect, as given by:

PS> [Array] | Get-Member -Static

   TypeName: System.Array

Name            MemberType Definition                                                                                       
----            ---------- ----------
Find            Method     static T Find[T](T[] array, System.Predicate[T] match)

Should the array be casted to something else to match the T[] type? I know there are other ways to achieve the find functionality, but I was wondering why this doesn't work.

5 Answers 5

43

There is no need to use Array.Find, a regular where clause would work fine:

$a = @(1,2,3,4,5)
$a | where { $_ -eq 3 }

Or this (as suggested by @mjolinor):

$a -eq 3

Or this (returns $true or $false):

$a -contains 3

Where clause supports any type of objects, not just basic types, like this:

$a | where { $_.SomeProperty -eq 3 }
Sign up to request clarification or add additional context in comments.

7 Comments

Because -eq also works as an array operator, it's even easier than that: $a -eq 3
There was a need for Array.Find, because the comparison needed to be done on an object instead of a primitive. The example was over-simplified.
Neolisk's approach is the powershell way of doing that, even with objects. Ex. $a | where { $_.MyProperty -eq "Foo" }.
@NoMoreMrCodeGuy: It does not matter, the above approach would work with any object. Use $_.Property to get a property value on the object.
@uSlackr: Pls ask a separate question, definitely needs more context.
|
17

You need to cast the ScriptBlock as a Predicate[T]. Consider the following example:

[Array]::Find(@(1,2,3), [Predicate[int]]{ $args[0] -eq 1 })
# Result: 1

The reason that you received the error, is because there was no matching method overload, in the case where you're passing in a PowerShell ScriptBlock. As you noted in your Get-Member output, there is no Find() method overload that accepts a ScriptBlock as its second parameter.

[Array]::Find(@(1,2,3), { $args[0] -eq 1 })

Cannot find an overload for "Find" and the argument count: "2". At line:1 char:17 + [Array]::Find(@(1,2,3), { $_ -eq 1 }) + ~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodException + FullyQualifiedErrorId : MethodCountCouldNotFindBest

1 Comment

The answer from @Neolisk is the more powershell-y way to do this.
5

Another option would be using an ArrayList, which provides a Contains method:

PS C:\> [Collections.ArrayList]$a = 'a', 'b', 'c'
PS C:\> $a.Contains('b')
True
PS C:\> $a.Contains('d')
False

Or, as @Neolisk mentioned in the comments, you could use PowerShell's -contains operator:

PS C:\> $a = 'a', 'b', 'c'
PS C:\> $a -contains 'b'
True
PS C:\> $a -contains 'd'
False

1 Comment

Powershell also has a Contains method, like this $a -contains 3.
4

This was run across ~6 million items in a system.array using both methods

$s=get-date
$([array]::FindALL($OPTArray,[Predicate[string]]{ $args[0] -match '^004400702(_\d{5})?' })).count
$(New-TimeSpan -Start $s -End $(get-date)).TotalSeconds

20 items
33.2223219 seconds

$s=get-date
$($OPTArray | where { $_ -match '^004400702(_\d{5})?'}).count 
$(New-TimeSpan -Start $s -End $(get-date)).TotalSeconds

20 items
102.1832173 seconds

Comments

1

Trevor Sullivan's answer is the right one, not only for Find() static method, but for FindIndex() as well.

When you've got several NIC cards with both ipv4 & ipv6 active on your servers and want to check the ipv4 IP/netmask pairs, something like this is good :

$NetworkAdapters = Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'IPEnabled = True' | Select-Object -Property Description, IPAddress, IPSubnet, DefaultIPGateway, DNSServerSearchOrder, DNSDomain
$NetworkAdapters | % {
  "Adapter {0} :" -f $_.Description
  # array'ing to avoid failure against single homed netcards
  $idx = [System.Array]::FindIndex(@($_.IPAddress), [Predicate[string]]{ $args[0] -match "\d+.\d+.\d+.\d+" })
  "  IP {0} has netmask {1}" -f @($_.IPAddress[$idx]), @($_.IPSubnet)[$idx]
}

My point is it works like a charm on 2012 WinPE, and fails on a production Win7 wks. Anyone got an idea ?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.