如何在一行内遍历此哈希?

时间:2012-01-25 03:17:46

标签: ruby hash enumerable ruby-1.9.3

哈希中的每个键都有一个也是哈希的值。

    {
      100 => {
        1 => 'ruby',
        2 => 'enumerables'
      },
      50 => {
        3 => 'can',
        4 => 'cause'
      },
      15 => {
        5 => 'occassional',
        6 => 'insanity'
      }
    }

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

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

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

5 个答案:

答案 0 :(得分:10)

这有效:

hash.values.inject(&:merge)

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

hash.values.reduce(:merge)

然后它变得简洁但非常易读。

答案 1 :(得分: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的评论 - 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

请注意,您的问题标题是遍历;如果你真的不想合并这些值 - 如果你想要两次访问一个重复的密钥而不是最后一次胜利 - 那么只需要:

original.values.each{ |h| h.each{ |k,v|
  # hey, we're traversing inside!
} }

答案 2 :(得分:3)

由于您不重视顶级键,因此使用#values来获取值的数组(在本例中,也是哈希值)。然后你可以使用#inject建立一个新的哈希值,然后合并它们。

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

可能有其他方法可以做到。

答案 3 :(得分:3)

Hash[original_hash.values.flat_map(&:to_a)]

答案 4 :(得分:2)

刚刚尝试了一下,首先尝试的是蛮力和更好的方法(在两个意思中......)都在那里。

h.map {|k,v| v}.inject({}) {|i,h| i.merge(h)}