2014-12-29 3 views
5

Ich habe Mühe, für mehrere Elemente Index des aktuellen Elements erhalten, die genau das gleiche Objekt sind:Array.Find und IndexOf für mehrere Elemente, die genau das gleiche Objekt sind

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

Alternative Version:

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

Dies wird zurückkehren: D D D

Aber dieser Code:

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

Alternative Version:

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

Returns:

so ist es bei dem Index des ersten Elements zeigt. Wie erhalten Sie Indizes der anderen Elemente?

+0

wenn niemand kommt mit einer besseren Lösung Sie könnten einfach eine for-Schleife verwenden, passen Sie hinein und erhalten Sie Ihren Index von der Zählervariable – Paul

+0

@Paul Das ist genau was ich vermeiden wollte: P – ALIENQuake

Antwort

9

Sie können dies tun:

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

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

1 
3 
6 
+1

ich mag es, schön und kurz. nur was mich nervt, ist das redundante Klammerpaar ('0 .. ($ b.count-1) |') wird es auch tun – Paul

+0

Der Versuch, die statischen Methoden von '[array]' zu verwenden, hat nicht die Einfachheit, die das macht. @ Paul hat niemanden verletzt, um explizit zu sein. – Matt

+0

mjolinor Danke @Matt Abgesehen davon ist diese Antwort wirklich nett, weißt du, wie man dies mit Methoden macht? – ALIENQuake

1

Sie noch eine Schleife mit den statischen Methoden von [array] brauchen würde, aber wenn Sie immer noch neugierig etwas wie diese sind funktionieren würde.

$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 

Schleife, bis keine Übereinstimmung gefunden wird. Nehmen Sie die Übereinstimmung zuerst an, indem Sie $singleIndex -1 zuweisen (was eine Nicht-Übereinstimmung zurückgibt). Wenn eine Übereinstimmung gefunden wird, fügen Sie den Index zu einem Ergebnis-Array hinzu.

+0

Ich war neugierig, danke! – ALIENQuake

3

mjolinor's answer ist konzeptuell elegant, aber langsam mit großen Arrays, was vermutlich auf einen ersten eine parallele Anordnung von Indizes zu bauen (die auch speicher ineffizient ist).

Es ist vom Konzept her ähnlich die folgenden LINQ-basierte Lösung (PSV3 +), die speichereffizienter und etwa doppelt so schnell ist, aber immer noch langsam:

$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' } 
) 

Während jedes Powershell Looping Lösung ist letztlich langsam im Vergleich zu einer kompilierten Sprache, die folgende Alternative, während ausführlicher, ist immer noch viel schneller mit großen Arrays:

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 

Hinweis:

  • Vielleicht überraschend, dass diese Lösung ist sogar schneller als Matt's solution, die [array]::IndexOf() in einer Schleife ruft stattdessen alle Elemente aufzuzählen.

  • Verwendung eines Skriptblock (mit Call Operator & und Argumenten aufgerufen), die zwar nicht unbedingt erforderlich ist, wird verwendet, die Verschmutzung der umgebenden Gültigkeitsbereich mit Helfer Variable $i zu verhindern.

  • Die foreachAnweisung ist schneller als die Foreach-ObjectCmdlets (dessen integrierten Aliase sind % und verwirrender, auch foreach).

  • Einfach (implizit) Ausgabe $i für jede Übereinstimmung macht PowerShell sammeln mehrere Ergebnisse in einem Array.

    • Wenn nur ein Index gefunden wird, erhalten Sie eine skalare [int] Instanz erhalten statt; Umgehen Sie den gesamten Befehl in @(...), um sicherzustellen, dass Sie immer ein Array erhalten.
  • Während $i selbst den Wert von $i, ++$i nach Design gibt nicht (obwohl Sie (++$i) das erreichen nutzen könnten, falls erforderlich).

  • Im Gegensatz zu Array.IndexOf(), die Powershell -eq Operator ist Fall- unempfindlich standardmäßig; Für Groß-/Kleinschreibung verwenden Sie stattdessen -ceq.


Es ist einfach, die oben in eine (einfache) Funktion (beachten Sie, dass die Parameter sind absichtlich nicht typisiert, für Flexibilität) zu drehen:

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 
Verwandte Themen