2017-01-27 11 views
-1

у меня есть такие хэш-таблицы:Как преобразовать хеш-таблицы во встроенные (вложенные) хэш-таблицы с Ruby?

h={"c4"=>1, "c8"=>2, "ec"=>3, "a"=>4, "e4"=>5, "1"=>6, "8"=>7} 

я могу получить доступ значение 2, как: h["c8"]

Я хотел бы преобразовать хэш-таблицу с вложенной хэш-таблицу, как это:

h={"c"=>{"4"=>1, "8"=>2}, "e"=>{"c"=>3, "4"=>5}, "a"=>4, "1"=>6, "8"=>7} 

Таким образом, я мог получить доступ к значению 2 как: h["c"]["8"] и всем остальным значениям аналогичным образом.

В целом вместо:

h["c8"] 

Я предпочел бы использовать:

h["c"]["8"] 

Потому что я хотел бы отметить строки в JavaScript. Поэтому я хотел бы создать очень большую встроенную хеш-таблицу с Ruby, выгрузить ее в JSON и загрузить в javascript. Такие встроенные хэш-таблицы легче искать, чем оригинальные. Ключи приходят из MD5, хешируя некоторые исходные значения, которые были именами файлов, а затем находили минимальные фрагменты с начала хэшированных ключей MD5, которые все еще uniq.

Еще больше пример:

h={"c4"=>1, 
"c8"=>2, 
"ec"=>3, 
"a8"=>4, 
"e4"=>5, 
"1"=>6, 
"8"=>7, 
"c9"=>8, 
"4"=>9, 
"d"=>10, 
"6"=>11, 
"c2"=>12, 
"c5"=>13, 
"aa"=>14} 

Было бы:

h={"c"=>{"4"=>1, "8"=>2, "9"=>8, "2"=>12, "5"=>13}, 
"e"=>{"c"=>3, "4"=>5}, 
"a"=>{"8"=>4, "a"=>14}, 
"1"=>6, 
"8"=>7, 
"4"=>9, 
"d"=>10, 
"6"=>11} 

Еще больше пример:

h={"c4"=>1, "c8"=>2, "ec"=>3, "a8"=>4, "e4"=>5, "16"=>6, "8f"=>7, "c9"=>8, "45"=>9, "d3"=>10, "65"=>11, "c2"=>12, "c5"=>13, "aa"=>14, "9b"=>15, "c7"=>16, "7"=>17, "6f"=>18, "1f0"=>19, "98"=>20, "3c"=>21, "b"=>22, "37"=>23, "1ff"=>24, "8e"=>25, "4e"=>26, "0"=>27, "33"=>28, "6e"=>29, "3417"=>30, "c1"=>31, "63"=>32, "18"=>33, "e3"=>34, "1c"=>35, "19"=>36, "a5b"=>37, "a57"=>38, "d67"=>39, "d64"=>40, "3416"=>41, "a1"=>42} 

быть бы:

h={"c"=>{"4"=>1, "8"=>2, "9"=>8, "2"=>12, "5"=>13, "7"=>16, "1"=>31}, 
"e"=>{"c"=>3, "4"=>5, "3"=>34}, 
"a"=>{"8"=>4, "a"=>14, "5"=>{"b"=>37, "7"=>38}, "1"=>42}, 
"1"=>{"6"=>6, "f"=>{"0"=>19, "f"=>24}, "8"=>33, "c"=>35, "9"=>36}, 
"8"=>{"f"=>7, "e"=>25}, 
"4"=>{"5"=>9, "e"=>26}, 
"d"=>{"3"=>10, "6"=>{"7"=>39, "4"=>40}}, 
"6"=>{"5"=>11, "f"=>18, "e"=>29, "3"=>32}, 
"9"=>{"b"=>15, "8"=>20}, 
"7"=>17, 
"3"=>{"c"=>21, "7"=>23, "3"=>28, "4"=>{"1"=>{"7"=>30, "6"=>41}}}, 
"b"=>22, 
"0"=>27} 

Моя попытка решить эту проблему немного некрасиво и использует «Eval», «ч» оригинальный хэш:

nested_hash={} 
h.keys.each{|k| 
    k.split(//).each_with_index{|b,i| 

    if nested_hash.dig(*k[0..i].split(//))==nil then 
     eval("nested_hash"+k[0..i].split(//).map{|z| "[\"#{z}\"]"}.join+"={}") 
    end 
    if i==k.size-1 then 
     eval("nested_hash"+k[0..i].split(//).map{|z| "[\"#{z}\"]"}.join+"=h[k]") 
    end 
    }; 
}; 
+1

Пожалуйста, прочитайте "[просить]" и «[mcve ]». Мы хотели бы видеть вашу попытку решения проблемы. Без этого похоже, что вы дали нам требования и ожидаете, что мы напишем код для вас. –

+0

Что было бы желаемым результатом, если 'h = {" a "=> 1," aa "=> 2}'? Возможно, '{" a "=> {" "=> 1," a "=> 2}'? –

+0

Эти комбинации клавиш в моем случае не допускаются, потому что процесс минимализации, который я применяю к преобразованным ключам MD5, не выделяет ключи, которые являются префиксом других ключей. Но в общем случае: да. – Konstantin

ответ

1

На основе комментариев по этому вопросу, размещенному ОП, я предполагал, что нет ключей k1 и k2, k2.size > k1.size, для которых k2[0, ki.size] == k1.

Код

def splat_hash(h) 
    h.select { |k,_| k.size > 1 }. 
    group_by { |k,_| k[0] }. 
    map { |k0,a| [k0, splat_hash(a.map { |k,v| [k[1..-1],v] }.to_h)] }. 
    to_h. 
    merge(h.select{ |k,_| k.size == 1 }) 
end 

Примеры

# 1

h = {"c4"=>1, "c8"=>2, "ec"=>3, "a"=>4, "e4"=>5, "1"=>6, "8"=>7} 
splat_hash h 
    #=> {"c"=>{"4"=>1, "8"=>2}, "e"=>{"c"=>3, "4"=>5}, "a"=>4, "1"=>6, "8"=>7} 

# 2

h = { "c4"=>1, "c8"=>2, "ec"=>3, "a8"=>4, "e4"=>5, "16"=>6, "8f"=>7, "c9"=>8, 
     "45"=>9, "d3"=>10, "65"=>11, "c2"=>12, "c5"=>13, "aa"=>14, "9b"=>15, 
     "c7"=>16, "7"=>17, "6f"=>18, "1f0"=>19, "98"=>20, "3c"=>21, "b"=>22, 
     "37"=>23, "1ff"=>24, "8e"=>25, "4e"=>26, "0"=>27, "33"=>28, "6e"=>29, 
     "3417"=>30, "c1"=>31, "63"=>32, "18"=>33, "e3"=>34, "1c"=>35, "19"=>36, 
     "a5b"=>37, "a57"=>38, "d67"=>39, "d64"=>40, "3416"=>41, "a1"=>42 } 
splat_hash h 
    #=> {"c"=>{"4"=>1, "8"=>2, "9"=>8, "2"=>12, "5"=>13, "7"=>16, "1"=>31}, 
    # "e"=>{"c"=>3, "4"=>5, "3"=>34}, 
    # "a"=>{"5"=>{"b"=>37, "7"=>38}, "8"=>4, "a"=>14, "1"=>42}, 
    # "1"=>{"f"=>{"0"=>19, "f"=>24}, "6"=>6, "8"=>33, "c"=>35, "9"=>36}, 
    # "8"=>{"f"=>7, "e"=>25}, 
    # "4"=>{"5"=>9, "e"=>26}, 
    # "d"=>{"6"=>{"7"=>39, "4"=>40}, "3"=>10}, 
    # "6"=>{"5"=>11, "f"=>18, "e"=>29, "3"=>32}, 
    # "9"=>{"b"=>15, "8"=>20}, 
    # "3"=>{"4"=>{"1"=>{"7"=>30, "6"=>41}}, "c"=>21, "7"=>23, "3"=>28}, 
    # "7"=>17, 
    # "b"=>22, 
    # "0"=>27} 

# 3

h = { "a"=>1, "ba"=>2, "bb"=>3, "caa"=>4, "cab"=>5, "daba"=>6, "dabb"=>7, "dabcde"=>8 } 
splat_hash h 
    #=> {"b"=>{"a"=>2, "b"=>3}, 
    # "c"=>{"a"=>{"a"=>4, "b"=>5}}, 
    # "d"=>{"a"=>{"b"=>{"c"=>{"d"=>{"e"=>8}},"a"=>6, "b"=>7}}}, 
    # "a"=>1} 

Объяснение

Я думаю, что лучший способ показать, что происходит, чтобы добавить некоторые puts заявления в код и запустить его на примере.

INDENT_SIZE = 6 

def putsi(str) 
    puts "#{' ' * @indent}#{str}" 
end 

def indent 
    @indent = (@indent ||= 0) + INDENT_SIZE 
end 

def undent 
    @indent -= INDENT_SIZE 
end 

def splat_hash(h) 
    puts 
    indent 
    putsi "enter splat_hash with h=#{h}" 
    h.select { |k,_| k.size > 1 }. 
    tap { |g| putsi " select > 1 = #{g}" }. 
    group_by { |k,_| k[0] }. 
    tap { |g| putsi " group_by = #{g}" }. 
    map { |k0,a| putsi " calling splat_hash"; 
      [k0, splat_hash(a.map { |k,v| [k[1..-1],v] }.to_h)] }. 
    tap { |a| putsi " map = #{a}" }.   
    to_h. 
    tap { |g| putsi " to_h = #{g}" }. 
    merge(h.select{ |k,_| k.size == 1 }). 
    tap { |g| putsi " returning g = #{g}" }. 
    tap { undent }   
end 

h = {"c4"=>1, "c8"=>2, "ec"=>3, "faa"=>4, "e4"=>5, "fab"=>6, "1"=>7 } 

splat_hash h 
    enter splat_hash with h={"c4"=>1, "c8"=>2, "ec"=>3, "faa"=>4, "e4"=>5, 
          "fab"=>6, "1"=>7} 
    select > 1 = {"c4"=>1, "c8"=>2, "ec"=>3, "faa"=>4, "e4"=>5, "fab"=>6} 
    group_by = {"c"=>[["c4", 1], ["c8", 2]], "e"=>[["ec", 3], ["e4", 5]], 
       "f"=>[["faa", 4], ["fab", 6]]} 
     calling splat_hash 

 enter splat_hash with h={"4"=>1, "8"=>2} 
      select > 1 = {} 
      group_by = {} 
      map = [] 
      to_h = {} 
      returning g = {"4"=>1, "8"=>2} 
     calling splat_hash 

 enter splat_hash with h={"c"=>3, "4"=>5} 
      select > 1 = {} 
      group_by = {} 
      map = [] 
      to_h = {} 
      returning g = {"c"=>3, "4"=>5} 
     calling splat_hash 

 enter splat_hash with h={"aa"=>4, "ab"=>6} 
      select > 1 = {"aa"=>4, "ab"=>6} 
      group_by = {"a"=>[["aa", 4], ["ab", 6]]} 
      calling splat_hash 

   enter splat_hash with h={"a"=>4, "b"=>6} 
       select > 1 = {} 
       group_by = {} 
       map = [] 
       to_h = {} 
       returning g = {"a"=>4, "b"=>6} 

      map = [["a", {"a"=>4, "b"=>6}]] 
      to_h = {"a"=>{"a"=>4, "b"=>6}} 
      returning g = {"a"=>{"a"=>4, "b"=>6}} 

    map = [["c", {"4"=>1, "8"=>2}], ["e", {"c"=>3, "4"=>5}], 
      ["f", {"a"=>{"a"=>4, "b"=>6}}]] 
    to_h = {"c"=>{"4"=>1, "8"=>2}, "e"=>{"c"=>3, "4"=>5}, "f"=>{"a"=>{"a"=>4, "b"=>6}}} 
    returning g = {"c"=>{"4"=>1, "8"=>2}, "e"=>{"c"=>3, "4"=>5}, 
        "f"=>{"a"=>{"a"=>4, "b"=>6}}, "1"=>7} 
#=> {"c"=>{"4"=>1, "8"=>2}, 
# "e"=>{"c"=>3, "4"=>5}, 
# "f"=>{"a"=>{"a"=>4, "b"=>6}}, "1"=>7} 
1

Это может быть сделано с помощью комбинации reduce и chars.

h={"c4"=>1, "c8"=>2, "ec"=>3, "a"=>4, "e4"=>5, "1"=>6, "8"=>7} 
result = h.reduce({}) do |memo, (k,v)| 
    key, nested_key = k.to_s.chars 
    if nested_key 
    memo[key] ||= {} 
    memo[key][nested_key] = v 
    else 
    memo[key] = v 
    end 
    memo 
end 
# => {"c"=>{"4"=>1, "8"=>2}, "e"=>{"c"=>3, "4"=>5}, "a"=>4, "1"=>6, "8"=>7} 

Если вы хотите иметь ключи более чем на 1 полукокса или более уровней вложенности, вам потребуется немного больше работы, но мы надеемся, что дает вам идею.

+0

Уровень вложенности определяется структурой клавиш может быть 5 или даже 10 или более. Предел равен 32, так как самый длинный ключ в этом сценарии 32 символа, потому что он исходит из хэша MD5, длина которого составляет 32 символа. – Konstantin

3

Вы описываете Trie. У меня был хороший опыт работы с triez и trie драгоценных камней.

Вам нужно перебрать пары key,value в ваш хэш и добавить строки md5 в trie со значением на листе.

В конце вы экспортируете всю структуру во вложенный хеш или определите to_json на узле trie.

PS: Ваш вопрос интересный и хорошо спрошенный.Вы не предоставили никакого кода, поэтому я не буду ни;)

+0

Возможно, стоит использовать драгоценный камень здесь, а не janky домашний код. – tadman

0

Ультра простой нерекурсивна (итерационный) решение вдохновленный JavaScript, переменная to действует как указатель на структуру:

def nested_hash(h) 
    bh={}; 
    h.keys.each{|k| 
     to=bh 
     k[0..-2].each_char{|c| 
      if to[c]==nil then 
      to[c]={} 
      end 
      to=to[c] 
     } 
     to[k[-1]]=h[k] 
    } 
    return bh 
end