如何使用Ruby将哈希表转换为嵌入式(嵌套)哈希表?

时间:2017-01-27 21:35:00

标签: json ruby hash md5

我有这样的哈希表:

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散列键的开头找到最小的片段,这些片段仍然是unq。

另一个较长的例子:

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”,“h”是原始哈希:

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
  };
};

4 个答案:

答案 0 :(得分:3)

您正在描述Trie。 我对trieztrie宝石有很好的体验。

您需要在哈希中迭代key,value对,并使用叶子上的值将md5字​​符串添加到trie中。

最后,您将整个结构导出到嵌套哈希或在特里节点上定义to_json

PS:你的问题很有意思,也很有问题。你没有提供任何代码,所以我也不会;)

答案 1 :(得分:1)

可以使用reducechars的组合来完成。

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个字符或更多嵌套级别的键,你需要多做一些工作,但这有希望给你一个想法。

答案 2 :(得分:1)

根据对OP发布的问题的评论,我假设没有k1k2k2.size > k1.size的密钥{{1} }。

<强>代码

k2[0, ki.size] == k1

<强>实施例

#1

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

#2

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} 

#3

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} 

<强>解释

我认为展示正在发生的事情的最佳方式是在代码中添加一些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

答案 3 :(得分: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