Чтобы дать вашей задаче имя: Вы ищете относительное дополнение иначе установить разницу между двумя массивами:
InОбозначения теории множеств: $ItemArray \ $ExclusionArray
, т. е. те элементы в $ItemArray
, которых нет в $ExclusionArray
.
Этот связанный вопрос ищет симметричная разница между двумя наборами, т. Е. Набор элементов, уникальных для любой стороны - наконец, это то, что реализуют решения на основе Compare-Object
, но только в предположении, чтокаждый массив имеет без дубликатов .
полезный ответ EyIM является концептуально простым и лаконичным .
A потенциальная проблема: производительность : поиск в массиве исключений должен выполняться для каждого элемента во входном массиве .
Для небольших массивов этоскорее всего, не будет иметь значения на практике.
При использовании больших массивов LINQ предлагает существенно быструюЭто решение :
Примечание : Чтобы воспользоваться преимуществами решения LINQ, ваши массивы должны быть в памяти уже , иПреимущество тем больше, чем больше массив исключений. Если ваши входные данные передаются по конвейеру, накладные расходы на его выполнение могут сделать попытки оптимизации обработки массива бессмысленными или даже контрпродуктивными, и в этом случае имеет смысл придерживаться собственного решения PowerShell - см. ответ iRon .
# Declare the arrays as [string[]]
# so that calling the LINQ method below works as-is.
# (You could also cast to [string[]] ad hoc.)
[string[]] $ItemArray = 'a','b','c','d'
[string[]] $exclusionArray = 'b','c'
# Return only those elements in $ItemArray that aren't also in $exclusionArray
# and convert the result (a lazy enumerable of type [IEnumerable[string]])
# back to an array to force its evaluation
# (If you directly enumerate the result in a pipeline, that step isn't needed.)
[string[]] [Linq.Enumerable]::Except($ItemArray, $exclusionArray) # -> 'a', 'd'
Обратите внимание на необходимость явного использования типов LINQ через их статические методы, поскольку PowerShell, начиная с v7, не поддерживает методы расширения . Тем не менее, есть предложение на GitHub , чтобы добавить такую поддержку; это связанное предложение запрашивает улучшенную поддержку для вызова универсальных методов.
См. этот ответ для обзора того, как в настоящее время вызывать методы LINQ из PowerShell.
Сравнение производительности:
Наконечник шляпы для iRon для его ввода.
В следующем эталонном коде используется Time-Command
функция для сравнения двух подходов, используя массивы примерно с 4000 и 2000 элементами соответственно, которые - как и в вопросе - отличаются только на 2 элемента.
Обратите внимание, что для выравниванияигровое поле, метод .Where()
массива (PSv4 +) используется вместо Where-Object
командлета на основе конвейера, так как .Where()
быстрее с массивами, уже находящимися в памяти.
Вот результаты, усредненные за 10 прогонов;обратите внимание на относительную производительность, как показано в столбцах Factor
;от одноядерной виртуальной машины Windows 10 с Windows PowerShell v5.1.:
Factor Secs (10-run avg.) Command TimeSpan
------ ------------------ ------- --------
1.00 0.046 # LINQ... 00:00:00.0455381
8.40 0.382 # Where ... -notContains... 00:00:00.3824038
Решение LINQ значительно быстрее - в 8 раз (хотя даже более медленное решение заняло всего около 0,4 секунды)для запуска).
Кажется, что разрыв в производительности еще больше в PowerShell Core , где я видел коэффициент около 19 с v7.0.0-preview.4 .;Интересно, что оба теста выполнялись быстрее по отдельности, чем в Windows PowerShell.
Код теста:
# Script block to initialize the arrays.
# The filler arrays are randomized to eliminate caching effects in LINQ.
$init = {
$fillerArray = 1..1000 | Get-Random -Count 1000
[string[]] $ItemArray = $fillerArray + 'a' + $fillerArray + 'b' + $fillerArray + 'c' + $fillerArray + 'd'
[string[]] $exclusionArray = $fillerArray + 'b' + $fillerArray + 'c'
}
# Compare the average of 10 runs.
Time-Command -Count 10 { # LINQ
. $init
$result = [string[]] [Linq.Enumerable]::Except($ItemArray, $exclusionArray)
}, { # Where ... -notContains
. $init
$result = $ItemArray.Where({ $exclusionArray -notcontains $_ })
}