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]

mklement0
  • 382,024
  • 64
  • 607
  • 775
rasonage
  • 53
  • 1
  • 1
  • 9
  • 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 – rasonage Nov 27 '20 at 01:11
  • 1
    In this [Automatic variable for the pipeline index `#13772`](https://github.com/PowerShell/PowerShell/issues/13772) idea, it would be something like: `$indices = $myarray | Foreach-Object { if ($_ -match $substring) { $PSIndex } }` – iRon Nov 27 '20 at 09:23

2 Answers2

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

If it was a file...

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

Line   LineNumber
----   ----------
herp            1
dederp          2
flerp           4
js2010
  • 23,033
  • 6
  • 64
  • 66
  • 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` – mklement0 Nov 27 '20 at 20:11