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

时间:2014-12-29 14:43:15

标签: arrays powershell indexing

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

$b = "A","D","B","D","C","E","D","F"
$b | ? { $_ -contains "D" }

替代版本:

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

这将返回: d d d

但是这段代码:

$b | % { $b.IndexOf("D") }

替代版本:

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

返回:

1 1 1

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

3 个答案:

答案 0 :(得分:10)

你可以这样做:

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

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

1
3
6

答案 1 :(得分:4)

mjolinor's answer 概念优雅,但大型数据慢,大概是因为必须首先建立一个并行的索引数组(这也是内存 - 低效)。

它在概念上类似于以下基于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' }
)

虽然与编译语言相比,任何 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

注意:

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

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

  • foreach 语句Foreach-Object cmdlet (其内置别名为%和,令人困惑的是,foreach)。

  • 简单地(隐式地)为每个匹配输出$i会使PowerShell在数组中收集多个结果。

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

  • Array.IndexOf()不同,PowerShell的-eq运算符默认情况下为case- 不敏感;区分大小写,请改用-ceq

将上述内容转换为(简单的)函数很容易(请注意,为了灵活性,这些参数是故意无类型的):

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

答案 2 :(得分:1)

你仍然需要使用[array]中的静态方法循环,​​但是如果你仍然好奇,那么这样的东西就可以了。

$b = "A","D","B","D","C","E","D","F"
$results = @()
$singleIndex = -1
Do{
    $singleIndex = [array]::IndexOf($b,"D",$singleIndex + 1)
    If($singleIndex -ge 0){$results += $singleIndex}
}While($singleIndex -ge 0)
$results

1
3
6

循环直到找不到匹配项。首先通过将$singleIndex指定为-1(这是非匹配将返回的内容)来假设匹配。找到匹配项时,将索引添加到结果数组中。