$a = 1..5
$b = 4..8
$Yellow = $a | Where {$b -NotContains $_}
$Yellow 包含$a 中的所有项目,$b 中的项目除外:
PS C:\> $Yellow
1
2
3
$Blue = $b | Where {$a -NotContains $_}
$Blue 包含$b 中的所有项目,$a 中的项目除外:
PS C:\> $Blue
6
7
8
$Green = $a | Where {$b -Contains $_}
没有问题,但无论如何; Green 包含$a 和$b 中的项目。
PS C:\> $Green
4
5
注意:Where 是Where-Object 的别名。别名可能会引入可能的问题并使脚本难以维护。
2019 年 10 月 12 日附录
正如@xtreampb 和@mklement0 评论的那样:尽管问题中的示例没有显示,问题所暗示的任务(值“不共同”)是两个输入集之间的对称差异(黄色和蓝色的结合)。
联合
$a 和 $b 之间的对称差异可以从字面上定义为 $Yellow 和 $Blue 的并集:
$NotGreen = $Yellow + $Blue
写出来的:
$NotGreen = ($a | Where {$b -NotContains $_}) + ($b | Where {$a -NotContains $_})
性能
您可能会注意到,此语法中有很多(冗余)循环:列表$a 中的所有项目迭代(使用Where)通过列表$b(使用-NotContains)中的项目,反之亦然.不幸的是,冗余是难以避免的,因为很难预测每一方的结果。哈希表通常是提高冗余循环性能的好解决方案。为此,我想重新定义问题:获取在集合总和中出现一次的值($a + $b):
$Count = @{}
$a + $b | ForEach-Object {$Count[$_] += 1}
$Count.Keys | Where-Object {$Count[$_] -eq 1}
通过使用ForEach 语句而不是ForEach-Object cmdlet 和Where 方法而不是Where-Object,您可以将性能提高2.5 倍:
$Count = @{}
ForEach ($Item in $a + $b) {$Count[$Item] += 1}
$Count.Keys.Where({$Count[$_] -eq 1})
LINQ
但Language Integrated Query (LINQ) 将轻松击败任何本机 PowerShell 和本机 .Net 方法(另请参阅 High Performance PowerShell with LINQ 和 mklement0 对 Can the following Nested foreach loop be simplified in PowerShell? 的回答:
要使用 LINQ,您需要显式定义数组类型:
[Int[]]$a = 1..5
[Int[]]$b = 4..8
并使用[Linq.Enumerable]:: 运算符:
$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))
基准测试
基准测试结果很大程度上取决于集合的大小以及实际共享的项目数量,作为“平均值”,我假设每个集合的一半是与另一个共享的。
Using Time
Compare-Object 111,9712
NotContains 197,3792
ForEach-Object 82,8324
ForEach Statement 36,5721
LINQ 22,7091
为了获得良好的性能比较,应该清除缓存,例如开始一个新的 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