Матрица.Найти и IndexOf для нескольких элементов, которые являются точно таким же объектом
у меня проблемы с получением индекса текущего элемента для нескольких элементов, которые являются точно таким же объектом:
$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
но этот код:
$b | % { $b.IndexOf("D") }
альтернативный вариант:
[Array]::FindAll($b, [Predicate[String]]{ $args[0] -contains "D" }) | % { $b.IndexOf($_) }
возвращает:
1 Один 1
таким образом, он указывает на индекс первого элемента. как получить индексы других элементов?
3 ответов
вы можете сделать это:
$b = "A","D","B","D","C","E","D","F"
(0..($b.Count-1)) | where {$b[$_] -eq 'D'}
1
3
6
mjolinor это is концептуально элегантных, а медленно с большими массивами, предположительно из-за необходимости сначала построить параллельный массив индексов (что также неэффективно для памяти).
это концептуально похоже на следующее решение на основе 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
Примечание:
возможно, удивительно, что это решение даже быстрее, чем Мэтт, которая называет
[array]::IndexOf()
в цикле вместо перечисления все элементы.использование a блок скрипта (вызывается оператором вызова
&
и аргументы), хотя и не является строго необходимым, используется для предотвращения загрязнения охватывающей области вспомогательной переменной$i
.на
foreach
сообщении быстрееForeach-Object
(чьи встроенные псевдонимы%
и, к сожалению, тоже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
вам все равно понадобится цикл со статическими методами из [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 (что и будет возвращено не совпадением). Когда найдено совпадение, добавьте индекс в массив результатов.