2015-04-23 2 views
0

Я пытаюсь создать более быстрый парсер для SOAP API, который превратит xml в хэш, и сопоставление ключей с загруженной памятью схемой на основе Структура YML. Я использовал Нори для разбора XML в хэш:Лучший способ получить значения из совпадающих ключей в рубиновом хеше

hash1 = { :key1 => { :@attr1=> "value1", :key2 => { :@attribute2 => "value2" }}} 

(старый синтаксис рубиновый, чтобы сохранить атрибуты из ключей ясно)

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

hash2 = {:key1 => { :key2 => { :@attribute2 => nil }}} 

(старый синтаксис рубин, чтобы сохранить атрибуты из ключей ясно)

Мне нужно совместить первый хэш со вторым, самым эффективным способом. Согласно моему пониманию, есть способы сделать это:

перебрать два хэш-ключей в то же время, но используя второй в качестве происхождения: (? Многострочного синтаксис, ¿ясный)

def iterate(hash2, hash1) 
    hash2.each do |k, v| 
    if v.is_a? Hash 
     iterate(hash2[k], hash1[k]) 
    else 
     hash2[k] = hash1[k] 
    end 
    end 
end 

Некоторые вопросы приходят на ум:

  • есть ли более эффективный способ сделать это без того, чтобы перебирать все мои ключи?
  • Это более эффективно, чем доступ к ключам напрямую?
  • Есть ли лучший способ проанализировать XML в хэш, используя hash2 внутри шаблона посетителя?
+0

Я не думает, что вы можете избежать итерации, когда у вас есть вложенные хэши, которые требуют рекурсию ... мне кажется, как только третий вопрос (редизайн) можно было бы изучить более эффективный путь ... может быть, есть способ избежать необходимости соответствовать хэшам ...? Или вы можете использовать StringScanner и написать свой собственный парсер ... – Myst

ответ

1

Решение без итерации может быть рекурсивным выбрать:

hash1 = { :key1 => { :@attr1=> "value1", 
        :key2 => { :@attribute2 => "value2" }, 
        :key3 => { :@attribute4 => "value4" } }, 
      :key2 => { :@attribute3 => "value3" } 
} 
hash2 = { :key1 => { :key2 => { :@attribute2 => nil }}, 
      :key2 => { :@attribute3 => nil } 
} 

def deep_select h1, h2 
    h1.select do |k, _| 
    h2.keys.include? k 
    end.map do |k, v| 
    v.is_a?(Hash) ? [k, deep_select(v, h2[k])] : [k, v] 
    end.to_h 
end 

puts deep_select hash1, hash2 
#⇒ {:key1=>{:key2=>{:@attribute2=>"value2"}}, :key2=>{:@attribute3=>"value3"}}} 

В общем, select должен быть лучше, чем each из-за сложного алгоритма выбора. В действительности разница составляет всего около 20%.

require 'benchmark' 

hash = (1..1_000_000).map { |i| ["key#{i}", i] }.to_h 
n = 5 

Benchmark.bm do |x| 
    garbage = 0 
    x.report { hash.each { |_, v| garbage += v } } 
    x.report { hash.select { |_, v| (v % 1000).zero? } } 
end 

#  user  system  total  real 
# 0.400000 0.000000 0.400000 ( 0.391305) 
# 0.320000 0.000000 0.320000 ( 0.321312) 
+0

Не выбрано ли итерация для поиска ключей al? – tebayoso

+0

@JorgedelosSantos Вообще говоря, 'select' может использовать интеллектуальный поиск. Обновлен ответ с помощью тестов. – mudasobwa

+0

20% - это много, когда речь идет о миллионах запросов. Звучит как ответ. – tebayoso

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