2016-10-07 3 views
1

У меня есть два массива, которые я бы хотел разделить. У меня был некоторый успех с COMPARE-OBJECT, но он слишком медленный для больших массивов. В этом примере $ ALLVALUES и $ ODD являются моими двумя массивами.Сравнение двух больших текстовых массивов в PowerShell

Раньше я был в состоянии сделать это эффективно, используя FINDSTR ex. FINDSTR/V /G:ODD.txt ALLVALUES.txt> EVEN.txt FINDSTR закончил это менее чем за 2 секунды для 110 000 элементов. (даже пришлось читать и записывать с диска)

Я пытаюсь вернуться к производительности FINDSTR, где он даст мне все в файле ALLVALUES.txt, который НЕ соответствует ODD.txt (давая мне значения EVEN в этот случай)

ПРИМЕЧАНИЕ. Этот вопрос касается не ODD или EVEN, а только практического примера, который может быть быстро и визуально проверен, что он работает по желанию.

Вот код, с которым я играл. Используя 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} 
+0

Метод хеширования размещен очень быстро. Спасибо, что ответили! Любые другие методы, которые следует учитывать с разумной скоростью? – BEEBUG

+0

Я добавил Compare-StringArray и улучшил код hashset. – wOxxOm

ответ

4

Массивы - это объекты, а не просто простые капли текста, которые определяют процессы findstr.
Самый быстрый diff строковых массивов - .NET3.5 + HashSet.SymmetricExceptWith.

$diff = [Collections.Generic.HashSet[string]]$a 
$diff.SymmetricExceptWith([Collections.Generic.HashSet[string]]$b) 
$diffArray = [string[]]$diff 

46 мс для элементов 100k на процессоре 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 мс, 8x медленнее, чем HashSet, но 110x быстрее, чем Compare-Object!

И, наконец, мы можем сделать быстрее 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 
      } 
     } 
    } 
} 

100k элементы: 20-28x быстрее, чем Compare-Object, завершается в 2100ms/1460ms (несортированный)
10k элементов: 2-3x быстрее, завершается в 210 мс/162 мс (несортировано)

Смежные вопросы