Ich wollte eine kleine Logik, um den Inhalt von zwei Arrays zu vergleichen und mit Powershell den Wert zu erhalten, der unter ihnen nicht üblich ist
Beispiel wenn
$a1=@(1,2,3,4,5)
$b1=@(1,2,3,4,5,6)
$ c, das die Ausgabe ist, sollte mir den Wert " 6
" geben, der die Ausgabe des ungewöhnlichen Werts zwischen beiden Arrays ist.
Kann mir jemand dabei helfen? Vielen Dank!
arrays
powershell
Power Shell
quelle
quelle
Compare-Object
Antworten hier implementieren: Der symmetrische Unterschied zwischen zwei Mengen wird bestimmt - aber nur, wenn die Eingabearrays wirklich Mengen sind (wie in der Frage), dh nein doppelte Elemente .Antworten:
PS > $c = Compare-Object -ReferenceObject (1..5) -DifferenceObject (1..6) -PassThru PS > $c 6
quelle
$keys = @($Null) * $ht.Keys.Count
ein Array mit der richtigen Größe zu initialisieren und dann$ht.Keys.CopyTo($keys, 0)
die Schlüssel in das Array zu kopieren.KeyCollection
to-object[]
Konvertierung durchführen, indem Sie den Wert einfach in@()
like einschließen@($keys)
.-PassThru
auch die interessierenden Eingabeelemente durchlaufen werden, werden sie zusätzlich mit einer Note-Eigenschaft dekoriertSideIndicator
, die in Szenarien wie der JSON-Serialisierung auftreten kann. Versuchen Sie es(Compare-Object 1 2 -PassThru).SideIndicator
.(Compare Object ...).InputObject
, wie in dieser Antwort , vermeidet dieses Problem.$ht1 = @{foo=1;bar=2}; $ht2 = @{foo=1;baz=3}; Compare-Object @($ht1.Keys) @($ht2.Keys)
$a = 1..5 $b = 4..8
$Yellow = $a | Where {$b -NotContains $_}
$Yellow
enthält alle Elemente in$a
außer denen, die in enthalten sind$b
:PS C:\> $Yellow 1 2 3
$Blue = $b | Where {$a -NotContains $_}
$Blue
enthält alle Elemente in$b
außer denen, die in enthalten sind$a
:PS C:\> $Blue 6 7 8
$Green = $a | Where {$b -Contains $_}
Nicht in Frage, aber trotzdem;
Green
enthält die Elemente, die in beiden$a
und enthalten sind$b
.PS C:\> $Green 4 5
Hinweis :
Where
ist ein Alias vonWhere-Object
. Alias kann mögliche Probleme verursachen und die Wartung von Skripten erschweren.Nachtrag 12. Oktober 2019
Wie von @xtreampb und @ mklement0 kommentiert: Obwohl aus dem Beispiel in der Frage nicht ersichtlich, ist die Aufgabe, die die Frage impliziert (Werte "nicht gemeinsam"), die symmetrische Differenz zwischen den beiden Eingabesätzen (die Vereinigung von Gelb und Blau). .
Union
Der symmetrische Unterschied zwischen
$a
und$b
kann wörtlich definiert werden als die Vereinigung von$Yellow
und$Blue
:$NotGreen = $Yellow + $Blue
Welches ist ausgeschrieben:
$NotGreen = ($a | Where {$b -NotContains $_}) + ($b | Where {$a -NotContains $_})
Performance
Wie Sie vielleicht bemerken, gibt es in dieser Syntax einige (redundante) Schleifen: Alle Elemente in der Liste
$a
durchlaufen (verwendenWhere
) Elemente in der Liste$b
(verwenden-NotConatins
) und umgekehrt. Leider ist die Redundanz schwer zu vermeiden, da es schwierig ist, das Ergebnis jeder Seite vorherzusagen. Eine Hash-Tabelle ist normalerweise eine gute Lösung, um die Leistung redundanter Schleifen zu verbessern. Dazu definiere ich gerne die Frage neu: Holen Sie sich die Werte, die einmal in der Summe der Sammlungen erscheinen ($a + $b
) :$Count = @{} $a + $b | ForEach-Object {$Count[$_] += 1} $Count.Keys | Where-Object {$Count[$_] -eq 1}
Wenn Sie die
ForEach
Anweisung anstelle desForEach-Object
Cmdlets und dieWhere
Methode anstelle von verwenden, könnenWhere-Object
Sie die Leistung um den Faktor 2,5 erhöhen:$Count = @{} ForEach ($Item in $a + $b) {$Count[$Item] += 1} $Count.Keys.Where({$Count[$_] -eq 1})
LINQ
Aber Language Integrated Query (LINQ) leicht schlagen alle nativen Powershell und nativen .NET - Methoden (siehe auch High Performance Powershell mit LINQ und mklement0 Antwort für Can folgende Nested foreach - Schleife in Powershell vereinfacht werden? :
Um LINQ verwenden zu können, müssen Sie die Array-Typen explizit definieren:
[Int[]]$a = 1..5 [Int[]]$b = 4..8
Und benutze den
[Linq.Enumerable]::
Operator:$Yellow = [Int[]][Linq.Enumerable]::Except($a, $b) $Blue = [Int[]][Linq.Enumerable]::Except($b, $a) $Green = [Int[]][Linq.Enumerable]::Intersect($a, $b) $NotGreen = [Int[]]([Linq.Enumerable]::Except($a, $b) + [Linq.Enumerable]::Except($b, $a))
Benchmark
Die Benchmark-Ergebnisse hängen stark von der Größe der Sammlungen und der Anzahl der tatsächlich freigegebenen Elemente ab. Als "Durchschnitt" gehe ich davon aus, dass die Hälfte jeder Sammlung mit der anderen geteilt wird.
Using Time Compare-Object 111,9712 NotContains 197,3792 ForEach-Object 82,8324 ForEach Statement 36,5721 LINQ 22,7091
Um einen guten Leistungsvergleich zu erhalten, sollten Caches gelöscht werden, indem beispielsweise eine neue PowerShell-Sitzung gestartet wird.
$a = 1..1000 $b = 500..1500 (Measure-Command { Compare-Object -ReferenceObject $a -DifferenceObject $b -PassThru }).TotalMilliseconds (Measure-Command { ($a | Where {$b -NotContains $_}), ($b | Where {$a -NotContains $_}) }).TotalMilliseconds (Measure-Command { $Count = @{} $a + $b | ForEach-Object {$Count[$_] += 1} $Count.Keys | Where-Object {$Count[$_] -eq 1} }).TotalMilliseconds (Measure-Command { $Count = @{} ForEach ($Item in $a + $b) {$Count[$Item] += 1} $Count.Keys.Where({$Count[$_] -eq 1}) }).TotalMilliseconds [Int[]]$a = $a [Int[]]$b = $b (Measure-Command { [Int[]]([Linq.Enumerable]::Except($a, $b) + [Linq.Enumerable]::Except($b, $a)) }).TotalMilliseconds
quelle
ForEach
Schleifen dafür erstellen könnten , aber am Ende ist es einfach :$NotGreen = $Yellow + $Blue
, was geschrieben steht:$NotGreen = ($a | Where {$b -NotContains $_}) + ($b | Where {$a -NotContains $_})
Compare-Object
Lösungen hier implementieren die symmetrische Differenz nur, wenn die Eingabearrays keine Duplikate aufweisen . Erwähnenswert ist auch: DieWhere-Object
/-not[contains]
-Lösungen sind konzeptionell einfach und prägnant, aber bei größeren Arrays kann dies zu einem Leistungsproblem führen, da für jedes Eingabeelement eine Array-Suche durchgeführt wird. LINQ bietet eine viel schnellere Lösung , obwohl diese etwas komplex ist.Ansehen
Compare-Object
Compare-Object $a1 $b1 | ForEach-Object { $_.InputObject }
Oder wenn Sie wissen möchten, wo das Objekt hingehört, schauen Sie sich SideIndicator an:
$a1=@(1,2,3,4,5,8) $b1=@(1,2,3,4,5,6) Compare-Object $a1 $b1
quelle
Compare-Object $a1 $b1 | ForEach-Object { $_.InputObject }
undCompare-Object $a1 $b1 -PassThru
scheinbar identische Ergebnisse erbringe. Natürlich ist die Option -PassThru prägnanter.-PassThru
auch die interessierenden Eingabeelemente weiterleiten , werden sie zusätzlich mit einer Note-Eigenschaft dekoriertSideIndicator
, die in unerwarteten Szenarien auftreten kann. Versuchen Sie es(Compare-Object 1 2 -PassThru).SideIndicator
.Versuchen:
$a1=@(1,2,3,4,5) $b1=@(1,2,3,4,5,6) (Compare-Object $a1 $b1).InputObject
Oder Sie können verwenden:
(Compare-Object $b1 $a1).InputObject
Die Reihenfolge spielt keine Rolle.
quelle
Ihre Ergebnisse sind nur dann hilfreich, wenn die Arrays zuerst sortiert werden. Um ein Array zu sortieren, führen Sie es über Sort-Object aus.
$x = @(5,1,4,2,3) $y = @(2,4,6,1,3,5) Compare-Object -ReferenceObject ($x | Sort-Object) -DifferenceObject ($y | Sort-Object)
quelle
Compare-Object $x $y
Gibt das gleiche Ergebnis wie oben zurück und zeigt, dass 6 im Referenzarray fehlt. (Ich habe dies sowohl ab der heutigen PS-Version (5.1) als auch ab der PS-Version 3 überprüft.)Dies sollte helfen, verwendet einfache Hash-Tabelle.
$a1=@(1,2,3,4,5) $b1=@(1,2,3,4,5,6) $hash= @{} #storing elements of $a1 in hash foreach ($i in $a1) {$hash.Add($i, "present")} #define blank array $c $c = @() #adding uncommon ones in second array to $c and removing common ones from hash foreach($j in $b1) { if(!$hash.ContainsKey($j)){$c = $c+$j} else {hash.Remove($j)} } #now hash is left with uncommon ones in first array, so add them to $c foreach($k in $hash.keys) { $c = $c + $k }
quelle