Ответ 1
PS > $c = Compare-Object -ReferenceObject (1..5) -DifferenceObject (1..6) -PassThru
PS > $c
6
Мне нужна небольшая логика для сравнения содержимого двух массивов и получения значения, которое не является общим среди них, используя powershell
пример, если
[email protected](1,2,3,4,5)
[email protected](1,2,3,4,5,6)
$c, который является результатом, должен дать мне значение "6
", которое является результатом того, что необычное значение между обоими массивами.
Может кто-нибудь помочь мне с тем же! благодарю!
PS > $c = Compare-Object -ReferenceObject (1..5) -DifferenceObject (1..6) -PassThru
PS > $c
6
$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
. Псевдоним может привести к возможным проблемам и усложнить поддержку сценариев.
Приложение от 12 октября 2019 года
Как прокомментировали @xtreampb и @mklement0: хотя это и не показано в примере, приведенном в вопросе, задача, о которой идет речь (значения "не общие"), заключается в симметричной разнице между двумя входными наборами (объединение желтого и синего).
Симметричное различие между $a
и $b
можно буквально определить как объединение $Yellow
и $Blue
:
$NotGreen = $Yellow + $Blue
Который выписан:
$NotGreen = ($a | Where {$b -NotContains $_}) + ($b | Where {$a -NotContains $_})
PerformanceКак вы могли заметить, в этом синтаксисе есть довольно много (избыточных) циклов: все элементы в списке $a
итерируют (используя Where
) через элементы в списке $b
(используя -NotConatins
) и наоборот. К сожалению, избыточности трудно избежать, так как трудно предсказать результат каждой стороны. Хэш-таблица обычно является хорошим решением для повышения производительности избыточных циклов. Для этого мне нравится переопределить вопрос: Получить значения, которые появляются один раз в сумме сборов ($a + $b
):
$Count = @{}
$a + $b | ForEach-Object {$Count[$_] += 1}
$Count.Keys | Where-Object {$Count[$_] -eq 1}
Используя оператор ForEach
вместо командлета ForEach-Object
и метод Where
вместо Where-Object
, вы можете повысить производительность в 2,5 раза:
$Count = @{}
ForEach ($Item in $a + $b) {$Count[$Item] += 1}
$Count.Keys.Where({$Count[$_] -eq 1})
Но Language Integrated Query (LINQ) легко превзойдет любые собственные методы PowerShell и собственные .Net (см. также Высокопроизводительный PowerShell с LINQ и ответ mklement0 для . Может ли следующий вложенный foreach упростить цикл в 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
Посмотрите Compare-Object
Compare-Object $a1 $b1 | ForEach-Object { $_.InputObject }
Или, если вы хотите узнать, к чему принадлежит объект, посмотрите на SideIndicator:
[email protected](1,2,3,4,5,8)
[email protected](1,2,3,4,5,6)
Compare-Object $a1 $b1
Try:
[email protected](1,2,3,4,5)
[email protected](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)
Это должно помочь, использует простую хеш-таблицу.
[email protected](1,2,3,4,5) [email protected](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
}