k7s5a's helpful answer ist konzeptionell elegant und bequem, aber es gibt eine caveat:
Es nicht gut skalierbar, weil ein Array-Lookup muss für jedes $a1
Element durchgeführt werden.
zumindest für größere Arrays, die Powershell Compare-Object
Cmdlets ist die bessere Wahl:
Wenn die Eingabefelder BEREITS sortiert:
(Compare-Object $a1 ($a2 + $a3) | Where-Object SideIndicator -eq '<=').InputObject
Hinweis:
* Compare-Object
doesn‘ t erfordert sortierte Eingabe, aber es kann die Leistung erheblich verbessern - siehe unten.
* Wie Esperento57 weist darauf hin, (Compare-Object $a1 ($a2 + $a3)).InputObject
zur Hand ausreichend im konkreten Fall ist, aber nur, weil $a2
und $a3
passieren keine Elemente enthalten, die in $a1
nicht auch sind.
Daher ist die allgemeinere Lösung Filter Where-Object SideIndicator -eq '<='
zu verwenden, da es die Ergebnisse auf Objekte beschränkt, die von der LHS fehlen ($a1
), und nicht umgekehrt.
Wenn die Eingabefelder nicht sortiert:
Explizit erweitert die Eingabefelder Sortierung vor ihnen stark zu vergleichen Leistung:
(Compare-Object ($a1 | Sort-Object) ($a2 + $a3 | Sort-Object) |
Where-Object SideIndicator -eq '<=').InputObject
Das folgende Beispiel, das verwendet eine 10.000- Elementarray, veranschaulicht den Unterschied in der Leistung:
$count = 10000 # Adjust this number to test scaling.
$a1 = 0..$($count-1) # With 10,000: 0..9999
$a2 = 0..$($count/2) # With 10,000: 0..5000
$a3 = $($count/2+1)..($count-3) # With 10,000: 5001..9997
$(foreach ($pass in 1..2) {
if ($pass -eq 1) {
$passDescr = "SORTED input"
} else {
$passDescr = "UNSORTED input"
# Shuffle the arrays.
$a1 = $a1 | Get-Random -Count ([int]::MaxValue)
$a2 = $a2 | Get-Random -Count ([int]::MaxValue)
$a3 = $a3 | Get-Random -Count ([int]::MaxValue)
}
[pscustomobject] @{
TestCategory = $passDescr
Test = "CompareObject, explicitly sorted first"
Timing = (Measure-Command {
(Compare-Object ($a1 | Sort-Object) ($a2 + $a3 | Sort-Object) | Where-Object SideIndicator -eq '<=').InputObject |
Out-Host; '---' | Out-Host
}).TotalSeconds
},
[pscustomobject] @{
TestCategory = $passDescr
Test = "CompareObject"
Timing = (Measure-Command {
(Compare-Object $a1 ($a2 + $a3) | Where-Object SideIndicator -eq '<=').InputObject |
Out-Host; '---' | Out-Host
}).TotalSeconds
},
[pscustomobject] @{
TestCategory = $passDescr
Test = "!.Contains(), two-pass"
Timing = (Measure-Command {
$a2AndA3 = $a2 + $a3
$a1 | Where-Object { !$a2AndA3.Contains($_) } |
Out-Host; '---' | Out-Host
}).TotalSeconds
},
[pscustomobject] @{
TestCategory = $passDescr
Test = "!.Contains(), two-pass, explicitly sorted first"
Timing = (Measure-Command {
$a2AndA3 = $a2 + $a3 | Sort-Object
$a1 | Sort-Object | Where-Object { !$a2AndA3.Contains($_) } |
Out-Host; '---' | Out-Host
}).TotalSeconds
},
[pscustomobject] @{
TestCategory = $passDescr
Test = "!.Contains(), single-pass"
Timing = (Measure-Command {
$a1 | Where-Object { !($a2 + $a3).Contains($_) } |
Out-Host; '---' | Out-Host
}).TotalSeconds
},
[pscustomobject] @{
TestCategory = $passDescr
Test = "-notcontains, two-pass"
Timing = (Measure-Command {
$a2AndA3 = $a2 + $a3
$a1 | Where-Object { $a2AndA3 -notcontains $_ } |
Out-Host; '---' | Out-Host
}).TotalSeconds
},
[pscustomobject] @{
TestCategory = $passDescr
Test = "-notcontains, two-pass, explicitly sorted first"
Timing = (Measure-Command {
$a2AndA3 = $a2 + $a3 | Sort-Object
$a1 | Sort-Object | Where-Object { $a2AndA3 -notcontains $_ } |
Out-Host; '---' | Out-Host
}).TotalSeconds
},
[pscustomobject] @{
TestCategory = $passDescr
Test = "-notcontains, single-pass"
Timing = (Measure-Command {
$a1 | Where-Object { ($a2 + $a3) -notcontains $_ } |
Out-Host; '---' | Out-Host
}).TotalSeconds
}
}) |
Group-Object TestCategory | ForEach-Object {
"`n=========== $($_.Name)`n"
$_.Group | Sort-Object Timing | Select-Object Test, @{ l='Timing'; e={ '{0:N3}' -f $_.Timing } }
}
Beispiel für die Ausgabe von meinem Rechner (Ausgabe von fehlenden Feldelemente weggelassen):
=========== SORTED input
Test Timing
---- ------
CompareObject 0.068
CompareObject, explicitly sorted first 0.187
!.Contains(), two-pass 0.548
-notcontains, two-pass 6.186
-notcontains, two-pass, explicitly sorted first 6.972
!.Contains(), two-pass, explicitly sorted first 12.137
!.Contains(), single-pass 13.354
-notcontains, single-pass 18.379
=========== UNSORTED input
CompareObject, explicitly sorted first 0.198
CompareObject 6.617
-notcontains, two-pass 6.927
-notcontains, two-pass, explicitly sorted first 7.142
!.Contains(), two-pass 12.263
!.Contains(), two-pass, explicitly sorted first 12.641
-notcontains, single-pass 19.273
!.Contains(), single-pass 25.174
Während Timings hängt von vielen Faktoren variieren, können Sie ein Gefühl bekommen, dass Compare-Object
Skalen viel besser, wenn Die Eingabe wird entweder vorsortiert oder auf Anforderung sortiert, und die Leistungslücke erweitert sich mit zunehmender Elementanzahl.
Wenn nichtCompare-Object
verwenden, kann die Leistung sein etwas erhöht - aber keinen Vorteil in der Lage zu nehmen der Sortierung wird die grundsätzlich limitierende Faktor:
Weder -notcontains
/-contains
noch .Contains()
kann vollen Vorteil der vorsortierten Eingabe nehmen.
Wenn der Eingang bereits sortierten: Mit der .Contains()
IList
Schnittstelle .NET Methode anstatt die Powershell -contains
/-notcontains
Operatoren (die eine frühere Version von K7S5A Antwort verwendet) verbessert die Leistung.
Joining Arrays $a2
und $a3
einmal vorne, und dann unter Verwendung der zusammengefügte Anordnung in der Pipeline Leistung verbessert (diese Weise die Anordnungen müssen nicht in jeder Iteration die verbunden werden sollen).