2017-06-09 2 views

Antwort

5

Versuchen Sie folgendes:

$a2AndA3 = $a2 + $a3 
$notInA2AndA3 = $a1 | Where-Object {!$a2AndA3.contains($_)} 

Als Motto:

$notInA2AndA3 = $a1 | Where {!($a2 + $a3).contains($_)} 
3

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 $a3einmal 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).

Verwandte Themen