$a = 1..5
$b = 4..8
$Yellow = $a | Where {$b -NotContains $_}
$Yellow
contient tous les éléments de $a
sauf ceux qui sont dans $b
:
PS C:\> $Yellow
1
2
3
$Blue = $b | Where {$a -NotContains $_}
$Blue
contient tous les éléments de $b
sauf ceux qui sont dans $a
:
PS C:\> $Blue
6
7
8
$Green = $a | Where {$b -Contains $_}
Pas en question, mais quand même ; Green
contient les éléments qui sont dans les deux $a
y $b
.
PS C:\> $Green
4
5
<em>Note </em>: <code>Where</code> est un alias de <code>Where-Object</code> . Les alias peuvent introduire des problèmes éventuels et rendre les scripts difficiles à maintenir.
Addendum 12 octobre 2019
Comme commenté par @xtreampb et @mklement0 : bien que non montré par l'exemple de la question, la tâche que la question implique (valeurs "pas en commun") est la différence symétrique entre les deux ensembles d'entrée (l'union de jaune et bleu) .
Union
La différence symétrique entre les $a
y $b
peut être littéralement définie comme l'union de $Yellow
y $Blue
:
$NotGreen = $Yellow + $Blue
Ce qui est écrit :
$NotGreen = ($a | Where {$b -NotContains $_}) + ($b | Where {$a -NotContains $_})
Performance
Comme vous pouvez le remarquer, cette syntaxe comporte un certain nombre de boucles (redondantes) : all items in list $a
itérer (en utilisant Where
) à travers les éléments de la liste $b
(en utilisant -NotContains
) et vice-versa. Malheureusement, la redondance est difficile à éviter car il est difficile de prédire le résultat de chaque côté. Une table de hachage est généralement une bonne solution pour améliorer les performances des boucles redondantes. Pour cela, j'aime redéfinir la question : Obtenez les valeurs qui apparaissent une fois dans la somme des collections ( $a + $b
) :
$Count = @{}
$a + $b | ForEach-Object {$Count[$_] += 1}
$Count.Keys | Where-Object {$Count[$_] -eq 1}
En utilisant le ForEach
au lieu de l'instruction ForEach-Object
et le Where
au lieu de la méthode Where-Object
vous pourriez augmenter les performances d'un facteur 2,5 :
$Count = @{}
ForEach ($Item in $a + $b) {$Count[$Item] += 1}
$Count.Keys.Where({$Count[$_] -eq 1})
LINQ
Mais Requête intégrée au langage (LINQ) battra facilement toutes les méthodes natives de PowerShell et de .Net (voir aussi PowerShell haute performance avec LINQ et la réponse de mklement0 pour La boucle foreach imbriquée suivante peut-elle être simplifiée en PowerShell ? :
Pour utiliser LINQ, vous devez définir explicitement les types de tableaux :
[Int[]]$a = 1..5
[Int[]]$b = 4..8
Et utilisez le [Linq.Enumerable]::
opérateur :
$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))
Point de repère
Les résultats des tests de référence dépendent fortement de la taille des collections et du nombre d'éléments réellement partagés. En moyenne, je suppose que la moitié de chaque collection est partagée avec l'autre.
Using Time
Compare-Object 111,9712
NotContains 197,3792
ForEach-Object 82,8324
ForEach Statement 36,5721
LINQ 22,7091
Pour obtenir une bonne comparaison des performances, les caches doivent être vidés, par exemple en démarrant une nouvelle session PowerShell.
$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