3

散列中的每个键都有一个也是散列的值。

    {
      100 => {
        1 => '红宝石',
        2 => '枚举'
      },
      50 => {
        3 => '可以',
        4 => '原因'
      },
      15 => {
        5 => '偶尔',
        6 => '精神错乱'
      }
    }

对于每个散列对象,我想丢弃顶级键,并将其替换为嵌套散列对象的键和值。

{
  1 => 'ruby',
  2 => 'enumerables',
  3 => 'can',
  4 => 'cause',
  5 => 'occasional',
  6 => 'insanity'
}

我让它工作,但我的方法使用merge!, 并且需要创建另一个哈希来存储值。我很好奇是否可以在一行中完成。我尝试使用reduce(),但无法使其工作。

4

6 回答 6

10

这有效:

hash.values.inject(&:merge)

编辑:另一个选项,使用reduce(与 相同inject),并注意 tokland 的注释,即当您使用符号时会自动调用 to_proc:

hash.values.reduce(:merge)

然后它变得不仅简洁而且非常易读。

于 2012-01-25T03:57:41.750 回答
5

我最喜欢@MarkThomas 的回答,但为了速度和内存效率,我建议:

flatter = {}.tap{ |h| original.values.each{ |h2| h.merge!(h2) } }

对当前答案的 200,000 次迭代进行基准测试表明这是最快的:

                          user     system      total        real
Phrogz                0.710000   0.020000   0.730000 (  0.728706)
Joshua Creek          0.830000   0.010000   0.840000 (  0.830700)
Mark Thomas symbol    1.460000   0.020000   1.480000 (  1.486463)
Mark Thomas to_proc   1.540000   0.030000   1.570000 (  1.565354)
Tim Peters            1.650000   0.030000   1.680000 (  1.678283)

由于@tokland 的评论——<code>original.values.reduce(:update)——修改了原始哈希,我们无法直接将其与其他方法进行比较。但是,如果我们修改所有测试以将第一个哈希的副本放回原始每次迭代中,@tokland 的答案将变得最快,尽管仍然不如我的快:

                          user     system      total        real
tokland's destroyer   0.760000   0.010000   0.770000 (  0.772774)
Phrogz                1.020000   0.020000   1.040000 (  1.034755)
Joshua Creek          1.060000   0.000000   1.060000 (  1.063874)
Mark Thomas symbol    1.780000   0.040000   1.820000 (  1.816909)
Mark Thomas to_proc   1.790000   0.030000   1.820000 (  1.819014)
Tim Peters            1.800000   0.040000   1.840000 (  1.827984)

如果您需要绝对速度并且可以修改原始值,请使用@tokland 的答案。如果您这样做并希望完好无损地保留原始未合并的哈希,那么您可以:

first_k,orig_v = original.each{ |k,v| break [k,v.dup] }
merged = original.values.reduce(:update)
original[first_k] = orig_v

请注意,您的问题标题是traverse;如果你真的不想合并这些值——如果你可能想访问重复键两次而不是最后一次获胜——那么只需执行以下操作:

original.values.each{ |h| h.each{ |k,v|
  # hey, we're traversing inside!
} }
于 2012-01-25T04:01:25.970 回答
3

由于您不重视顶级键,因此使用 #values 来获取值的数组(在这种情况下,也是哈希)。然后你可以使用#inject 建立一个新的散列,在你进行的时候合并它们。

yourhash.values.inject{|hash, thing| hash.merge(thing)}

可能还有其他方法可以做到这一点。

于 2012-01-25T03:27:53.430 回答
3
Hash[original_hash.values.flat_map(&:to_a)]
于 2012-01-25T19:42:39.980 回答
2

只是试了一下,第一次尝试是蛮力和更好的方法(在这个词的两个意义上......)就在那里。

h.map {|k,v| v}.inject({}) {|i,h| i.merge(h)}
于 2012-01-25T03:28:20.813 回答
2

虽然它不像其他一些答案那么简短,但我认为each_with_object值得一提。

output = input.each_with_object Hash.new do |(_,subhash), hash|
  hash.merge! subhash
end
于 2012-01-25T04:35:53.993 回答