Подтвердить что ты не робот

Сравнение двух массивов и получения значений, которые не являются общими

Мне нужна небольшая логика для сравнения содержимого двух массивов и получения значения, которое не является общим среди них, используя powershell

пример, если

[email protected](1,2,3,4,5)
[email protected](1,2,3,4,5,6)

$c, который является результатом, должен дать мне значение "6", которое является результатом того, что необычное значение между обоими массивами.

Может кто-нибудь помочь мне с тем же! благодарю!

4b9b3361

Ответ 1

PS > $c = Compare-Object -ReferenceObject (1..5) -DifferenceObject (1..6) -PassThru
PS > $c
6

Ответ 2

Collection

$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

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

LINQ

Но 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))

Benchmark

Результаты эталонных тестов сильно зависят от размеров коллекций и от того, сколько предметов на самом деле являются общими. В качестве "среднего" я предполагаю, что половина каждой коллекции используется совместно с другой.

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

Ответ 3

Посмотрите 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

Ответ 4

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

Порядок не имеет значения.

Ответ 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)

Ответ 6

Это должно помочь, использует простую хеш-таблицу.

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