#arrays #powershell
#массивы #powershell
Вопрос:
У меня есть два массива, между которыми я хотел бы использовать разницу. У меня был некоторый успех с COMPARE-OBJECT, но он слишком медленный для больших массивов. В этом примере $ALLVALUES и $ ODD — это два моих массива.
Раньше я мог эффективно делать это, используя FINDSTR ex. FINDSTR /V /G:ODD.txt ALLVALUES.txt > EVEN.txt FINDSTR выполнил это менее чем за 2 секунды для 110 000 элементов. (даже приходилось читать и записывать с диска)
Я пытаюсь вернуться к производительности FINDSTR, где это дало бы мне все ALLVALUES.txt это НЕ соответствовало ODD.txt (давая мне ЧЕТНЫЕ значения в этом случае)
ПРИМЕЧАНИЕ: Этот вопрос касается не ЧЕТНОГО или нечетного, а только практического примера, который можно быстро и визуально проверить, работает ли он так, как требуется.
Вот код, с которым я играл. Используя COMPARE-OBJECT, 100 000 заняло около 200 секунд против 2 секунд для FINDSTR на моем компьютере. Я думаю, что есть гораздо более элегантный способ сделать это в PowerShell. Спасибо за вашу помощь.
# ------- Build the MAIN array
$MIN = 1
$MAX = 100000
$PREFIX = "AA"
$ALLVALUES = while ($MIN -le $MAX)
{
"$PREFIX{0:D6}" -f $MIN
}
# ------- Build the ODD values from the MAIN array
$MIN = 1
$MAX = 100000
$PREFIX = "AA"
$ODD = while ($MIN -le $MAX)
{
If ($MIN%2) {
"$PREFIX{0:D6}" -f $MIN
}
ELSE {
$MIN
}
}
Measure-Command{$EVEN = Compare-Object -DifferenceObject $ODD -ReferenceObject $ALLVALUES -PassThru}
Комментарии:
1. Опубликованный метод хэширования выполняется очень быстро. Спасибо за ответ! Любые другие методы, которые следует учитывать с разумной скоростью?
2. Я добавил Compare-stringArray и улучшил код hashset.
Ответ №1:
Массивы — это объекты, а не просто большие двоичные объекты текста, которые обрабатываются findstr.
Самая быстрая разница между строковыми массивами — .NET3.5 HashSet.SymmetricExceptWith.
$diff = [Collections.Generic.HashSet[string]]$a
$diff.SymmetricExceptWith([Collections.Generic.HashSet[string]]$b)
$diffArray = [string[]]$diff
46 мс для 100 тыс. элементов на процессоре i7 с использованием ваших данных.
Приведенный выше код не содержит повторяющихся значений, поэтому, если они необходимы в выходных данных, я думаю, нам придется использовать гораздо более медленное перечисление вручную.
function Diff-Array($a, $b, [switch]$unique) {
if ($unique.IsPresent) {
$diff = [Collections.Generic.HashSet[string]]$a
$diff.SymmetricExceptWith([Collections.Generic.HashSet[string]]$b)
return [string[]]$diff
}
$occurrences = @{}
foreach ($_ in $a) { $occurrences[$_] }
foreach ($_ in $b) { $occurrences[$_]-- }
foreach ($_ in $occurrences.GetEnumerator()) {
$cnt = [Math]::Abs($_.value)
while ($cnt--) { $_.key }
}
}
Использование:
$diffArray = Diff-Array $ALLVALUES $ODD
340 мс, в 8 раз медленнее, чем hashset, но в 110 раз быстрее, чем Compare-Object!
И, наконец, мы можем создать более быстрый объект сравнения для массивов строк / чисел:
function Compare-StringArray($a, $b, [switch]$unsorted) {
$occurrences = if ($unsorted.IsPresent) { @{} }
else { [Collections.Generic.SortedDictionary[string,int]]::new() }
foreach ($_ in $a) { $occurrences[$_] }
foreach ($_ in $b) { $occurrences[$_]-- }
foreach ($_ in $occurrences.GetEnumerator()) {
$cnt = $_.value
if ($cnt) {
$diff = [PSCustomObject]@{
InputObject = $_.key
SideIndicator = if ($cnt -lt 0) { '=>' } else { '<=' }
}
$cnt = [Math]::Abs($cnt)
while ($cnt--) {
$diff
}
}
}
}
100 тыс. элементов: в 20-28 раз быстрее, чем Compare-Object, выполняется за 2100 мс / 1460 мс (без сортировки)
10 тыс. элементов: в 2-3 раза быстрее, завершается за 210 мс / 162 мс (без сортировки)
Комментарии:
1. потрясающее решение, обработано 1,4 млн строк против 600 тыс. за 30 секунд, в моем случае я использовал IntersectWith, потому что я хотел пересечение