【问题标题】:Comparing two arrays & get the values which are not common比较两个数组并获取不常见的值
【发布时间】:2011-09-16 03:19:36
【问题描述】:

我想要一个小逻辑来比较两个数组的内容并使用 powershell 获取它们之间不常见的值

例子

$a1=@(1,2,3,4,5)
$b1=@(1,2,3,4,5,6)

$c 作为输出应该给我值“6”,这是两个数组之间不常见值的输出。

谁能帮我解决这个问题!谢谢!

【问题讨论】:

  • 为手头的任务命名,至少就Compare-Object 此处的答案实现而言:正在确定两组之间的symmetric difference - 但前提是输入数组是真正的 sets(如问题所示),即 没有重复的元素
  • 一项相关任务 - relative complement aka set 差异 - 一个集合中的哪些元素不在另一个集合中? - 是this related question的主题。

标签: arrays powershell


【解决方案1】:
PS > $c = Compare-Object -ReferenceObject (1..5) -DifferenceObject (1..6) -PassThru
PS > $c
6

【讨论】:

  • 给那些试图比较两个哈希表的 Keys 集合的人注意:我假设 Keys 集合就像数组,我可以使用 Compare-Object 来比较它们。事实证明,Compare-Object 将每个 Keys 集合视为一个对象,因此返回一个结果,表明哈希表 1 中的 all 键从哈希表 2 中丢失,反之亦然。为了让它工作,我必须将 Keys 集合转换为数组。我发现的最快方法是:$keys = @($Null) * $ht.Keys.Count 初始化正确大小的数组,然后$ht.Keys.CopyTo($keys, 0) 将密钥复制到数组中。
  • 看起来你可以通过像@($keys)一样将@()中的值包装起来来进行KeyCollectionobject[]的转换。
  • 很好的解决方案,小警告:虽然-PassThru 也传递感兴趣的输入元素,但它还用SideIndicator 可能出现在场景中的注释属性装饰比如 JSON 序列化。试试(Compare-Object 1 2 -PassThru).SideIndicator(Compare Object ...).InputObjectthis answer 一样,避免了这个问题。
  • @SimonTewsi:mdonoughe 是正确的;说明:$ht1 = @{foo=1;bar=2}; $ht2 = @{foo=1;baz=3}; Compare-Object @($ht1.Keys) @($ht2.Keys)
【解决方案2】:

$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

注意WhereWhere-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

【讨论】:

  • 不常见的也可能被证明是有用的 (!green)。那么什么只有黄色或蓝色(1,2,3,6,7,8)
  • @xtreampb,我已经对您的建议提出了一些想法,并得出结论,您可能会为此创建各种复杂的嵌入式 ForEach 循环,但最终它很简单:$NotGreen = $Yellow + $Blue ,写出:$NotGreen = ($a | Where {$b -NotContains $_}) + ($b | Where {$a -NotContains $_})
  • 补充@xtreampb 的评论:问题暗示的任务(值“不共同”)是两个输入集之间的对称差异黄色和蓝色的联合)。这就是这里的其他答案所实现的,而你的实现不同:相对补码 / 设置差异(黄色或蓝色)和交集 - 虽然你很好地说明了这些。我建议在答案中明确说明。
  • 澄清:这里的Compare-Object 解决方案仅在输入数组没有重复项时才实现对称差异。还值得一提:Where-Object / -not[contains] 解决方案在概念上简单明了,但由于对每个输入元素 - LINQ offers a much faster solution 执行数组查找,较大的数组可能会带来性能问题,尽管它有点复杂。
  • @mklement0,感谢您的澄清并指出了对称差异的实际请求,我错过了(部分原因是它不是来自问题中的示例) .我已经进行了一些性能测试,并将在本周末更新我的答案。
【解决方案3】:

Compare-Object

Compare-Object $a1 $b1 | ForEach-Object { $_.InputObject }

或者如果你想知道对象属于哪里,那么看看 SideIndicator:

$a1=@(1,2,3,4,5,8)
$b1=@(1,2,3,4,5,6)
Compare-Object $a1 $b1

【讨论】:

  • 添加 -PassThru 选项使其输出更好。比较对象 $a1 $b1 -PassThru
  • 据我所知,Compare-Object $a1 $b1 | ForEach-Object { $_.InputObject }Compare-Object $a1 $b1 -PassThru 似乎产生了相同的输出。当然,-PassThru 选项更简洁。
  • @SimonTewsi:它们几乎相同:虽然-PassThru 也传递感兴趣的输入元素,但它还用装饰它们 SideIndicator note 属性可能会在意外情况下出现。试试(Compare-Object 1 2 -PassThru).SideIndicator
【解决方案4】:

试试:

$a1=@(1,2,3,4,5)
$b1=@(1,2,3,4,5,6)
(Compare-Object $a1 $b1).InputObject

或者,您可以使用:

(Compare-Object $b1 $a1).InputObject

顺序无关紧要。

【讨论】:

    【解决方案5】:

    除非首先对数组进行排序,否则您的结果将无济于事。 要对数组进行排序,请通过 Sort-Object 运行它。

    $x = @(5,1,4,2,3)
    $y = @(2,4,6,1,3,5)
    
    Compare-Object -ReferenceObject ($x | Sort-Object) -DifferenceObject ($y | Sort-Object)
    

    【讨论】:

    • -SyncWindow 有助于“在数组中查找匹配的距离”
    • 不,不需要排序:Compare-Object $x $y 将返回与上面相同的结果,表明引用数组中缺少 6。 (我在今天的 PS 版本(5.1)和 PS 版本 3 都检查了这一点。)
    【解决方案6】:

    这应该有帮助,使用简单的哈希表。

    $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
    }
    

    【讨论】:

    • 撇开有问题的编码风格,使用哈希表代替 -contains 运算符是不行的。最糟糕的是,此解决方案不会向 compare-object 添加任何内容。
    猜你喜欢
    • 2017-06-01
    • 1970-01-01
    • 2021-09-04
    • 1970-01-01
    • 2018-01-25
    • 2021-07-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多