3

Is there another way return the indices of every instance of a substring in an array besides old fashioned looping through indices?

$myarray = @('herp','dederp','dedoo','flerp')
$substring = 'erp'
$indices = @()
for ($i=0; $i -lt $myarray.length; $i++) {
    if ($myarray[$i] -match $substring){
       $indices = $indices + $i
   }
}


$thisiswrong = @($myarray.IndexOf($substring))

The conditional inside that kind of for loop is kinda cumbersome, and $thisiswrong only ever gets a value of [-1]

2
  • 1
    yep it was a copy paste error $flarray is the real variable in my script, $myarray is what I used to ask a question, sorry about that. Thanks mklement0 Commented Nov 27, 2020 at 1:11
  • 1
    In this Automatic variable for the pipeline index #13772 idea, it would be something like: $indices = $myarray | Foreach-Object { if ($_ -match $substring) { $PSIndex } } Commented Nov 27, 2020 at 9:23

2 Answers 2

3

You can use LINQ (adapted from this C# answer):

$myarray = 'herp', 'dederp', 'dedoo', 'flerp'
$substring = 'erp'

[int[]] $indices = [Linq.Enumerable]::Range(0, $myarray.Count).
                     Where({ param($i) $myarray[$i] -match $substring })

$indices receives 0, 1, 3.


As for what you tried:

$thisiswrong = @($myarray.IndexOf($substring))

System.Array.IndexOf only ever finds one index and matches entire elements, literally and case-sensitively in the case of strings.


There's a more PowerShell-like, but much slower alternative, as hinted at by js2010's answer; you can take advantage of the fact that the match-information objects that Select-String outputs have a .LineNumber property, reflecting the 1-based index of the position in the input collection - even if the input doesn't come from a file:

$myarray = 'herp', 'dederp', 'dedoo', 'flerp'
$substring = 'erp'

[int[]] $indices = 
  ($myarray | Select-String $substring).ForEach({ $_.LineNumber - 1 })

Note the need to subtract 1 from each .LineNumber value to get the 0-based array indices, and the use of the .ForEach() array method, which performs better than the ForEach-Object cmdlet.

Sign up to request clarification or add additional context in comments.

Comments

0

If it was a file...

get-content file | select-string erp | select line, linenumber

Line   LineNumber
----   ----------
herp            1
dederp          2
flerp           4

1 Comment

Nice, but worth mentioning explicitly that line numbers are 1-, not 0-based. If that isn't a concern, you don't even need a file: 'herp','dederp','dedoo','flerp' | select-string erp | select line, linenumber

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.