我想要一个小的逻辑来比较两个数组的内容并使用Powershell获得其中不常见的值
例如
$a1=@(1,2,3,4,5)
$b1=@(1,2,3,4,5,6)
输出的$ c应该给我值“ 6
”,这是两个数组之间不常见值的输出。
有人可以帮我吗!谢谢!
Answers:
PS > $c = Compare-Object -ReferenceObject (1..5) -DifferenceObject (1..6) -PassThru
PS > $c
6
$keys = @($Null) * $ht.Keys.Count
初始化一个正确大小的数组,然后$ht.Keys.CopyTo($keys, 0)
将Keys复制到该数组。
KeyCollection
到object[]
的只是包装的价值转换@()
一样@($keys)
。
$ht1 = @{foo=1;bar=2}; $ht2 = @{foo=1;baz=3}; Compare-Object @($ht1.Keys) @($ht2.Keys)
$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所评论的:尽管未从问题示例中显示,但该问题所隐含的任务(值为“ not common”)是两个输入集(黄色和蓝色的并集)之间的对称差异。。
$a
和之间的对称差$b
可以从字面上定义为$Yellow
和的并集$Blue
:
$NotGreen = $Yellow + $Blue
写出:
$NotGreen = ($a | Where {$b -NotContains $_}) + ($b | Where {$a -NotContains $_})
您可能会注意到,此语法中有很多循环(冗余):列表中的所有项目通过列表中的项目$a
(使用Where
)遍历$b
(使用-NotConatins
),反之亦然。不幸的是,由于难以预测每一侧的结果,因此很难避免冗余。哈希表通常是提高冗余循环性能的良好解决方案。为此,我想重新定义问题:获取在集合的总和($a + $b
)中出现一次的值:
$Count = @{}
$a + $b | ForEach-Object {$Count[$_] += 1}
$Count.Keys | Where-Object {$Count[$_] -eq 1}
通过使用ForEach
语句而不是ForEach-Object
cmdlet和Where
方法而不是cmdlet,Where-Object
可以将性能提高2.5倍:
$Count = @{}
ForEach ($Item in $a + $b) {$Count[$Item] += 1}
$Count.Keys.Where({$Count[$_] -eq 1})
但是语言集成查询(LINQ)可以轻松击败任何本机PowerShell和本机.Net方法(另请参见具有LINQ的高性能PowerShell和mklement0的答案,可以在PowerShell中简化以下嵌套的foreach循环吗?:
要使用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
ForEach
为此创建各种复杂的嵌入式循环,但最后它很简单:$NotGreen = $Yellow + $Blue
,其写为:$NotGreen = ($a | Where {$b -NotContains $_}) + ($b | Where {$a -NotContains $_})
Compare-Object
如果输入数组没有重复项,则此处的解决方案仅实现对称差。还值得一提的是Where-Object
/-not[contains]
解决方案在概念上简单明了,但是对于较大的数组,由于对每个输入元素都执行数组查找,因此可能会带来性能问题-LINQ提供了更快的解决方案,尽管有些复杂。
看着 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
Compare-Object $a1 $b1 | ForEach-Object { $_.InputObject }
并Compare-Object $a1 $b1 -PassThru
似乎产生相同的输出。当然,-PassThru选项更加简洁。
-PassThru
还传递了感兴趣的输入元素,但它还用note属性装饰了它们,这些SideIndicator
属性可能会在意外情况下浮出水面。尝试(Compare-Object 1 2 -PassThru).SideIndicator
。
尝试:
$a1=@(1,2,3,4,5)
$b1=@(1,2,3,4,5,6)
(Compare-Object $a1 $b1).InputObject
或者,您可以使用:
(Compare-Object $b1 $a1).InputObject
顺序无关紧要。
除非先对数组进行排序,否则您的结果将无济于事。要对数组排序,请通过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)
Compare-Object $x $y
将返回相同的结果如上述,显示出6从参考阵列丢失。(我从今天的PS版本(5.1)到PS版本3都对此进行了检查。)
这应该有所帮助,使用简单的哈希表。
$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
}
Compare-Object
这里实现的答案:确定两个集合之间的对称差异-但前提是输入数组是真正的集合(如问题中所述),即没有重复元素。