Array.Find和IndexOf用于完全相同对象的多个元素

ALI*_*ake 7 arrays indexing powershell

我无法获得完全相同对象的多个元素的当前元素的索引:

$b = "A","D","B","D","C","E","D","F"
$b | ? { $_ -contains "D" }
Run Code Online (Sandbox Code Playgroud)

替代版本:

$b = "A","D","B","D","C","E","D","F"
[Array]::FindAll($b, [Predicate[String]]{ $args[0] -contains "D" })
Run Code Online (Sandbox Code Playgroud)

这将返回:D D D.

但是这段代码:

$b | % { $b.IndexOf("D") }
Run Code Online (Sandbox Code Playgroud)

替代版本:

[Array]::FindAll($b, [Predicate[String]]{ $args[0] -contains "D" }) | % { $b.IndexOf($_) }
Run Code Online (Sandbox Code Playgroud)

返回:

1 1 1

所以它指向第一个元素的索引.如何获取其他元素的索引?

mjo*_*nor 10

你可以这样做:

$b = "A","D","B","D","C","E","D","F" 

(0..($b.Count-1)) | where {$b[$_] -eq 'D'}

1
3
6
Run Code Online (Sandbox Code Playgroud)


mkl*_*nt0 5

mjolinor 的答案概念上很优雅,但对于大数组来说很慢,大概是因为必须首先构建一个并行的索引数组(这也是内存效率低下的)。

它在概念上类似于以下基于 LINQ 的解决方案(PSv3+),其内存效率更高,速度大约是其两倍,但仍然很慢

$arr = 'A','D','B','D','C','E','D','F'
[Linq.Enumerable]::Where(
 [Linq.Enumerable]::Range(0, $arr.Length), 
   [Func[int, bool]] { param($i) $arr[$i] -eq 'D' }
)
Run Code Online (Sandbox Code Playgroud)

虽然与编译语言相比,任何PowerShell 循环解决方案最终都比较慢,但以下替代方案虽然更加冗长,但对于大型数组仍然要快得多

PS C:\> & { param($arr, $val)
         $i = 0
         foreach ($el in $arr) { if ($el -eq $val) { $i } ++$i }
        } ('A','D','B','D','C','E','D','F') 'D'
1
3
6
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 也许令人惊讶的是,这个解决方案甚至比Matt 的解决方案更快,后者[array]::IndexOf()在循环中调用而不是枚举所有元素。

  • 使用脚本块(使用调用运算符&和参数调用)虽然不是绝对必要的,但用于防止辅助变量污染封闭范围$i

  • foreach 语句Foreach-Object cmdlet更快(其内置别名是%and,令人困惑的是,还有foreach)。

  • $i每个匹配项的简单(隐式)输出使 PowerShell 将多个结果收集到一个数组中。

    • 如果只找到一个索引,你会得到一个标量[int]实例;将整个命令包装起来@(...)以确保您始终获得一个数组。
  • 虽然$i它本身输出 的值$i++$i但设计上不会(尽管(++$i)如果需要,您可以使用它来实现)。

  • 与 不同Array.IndexOf(),PowerShell 的-eq运算符默认不区分大小写;为了区分大小写,请-ceq改用。


很容易把上面的变成一个(简单的)函数(注意参数是故意无类型的,为了灵活性):

function get-IndicesOf($Array, $Value) {
  $i = 0
  foreach ($el in $Array) { 
    if ($el -eq $Value) { $i } 
    ++$i
  }
}
Run Code Online (Sandbox Code Playgroud)
# Sample call
PS C:\> get-IndicesOf ('A','D','B','D','C','E','D','F') 'D'
1
3
6
Run Code Online (Sandbox Code Playgroud)