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

Mar*_*rco 3 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'
}
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*mas 10

这有效:

hash.values.inject(&:merge)
Run Code Online (Sandbox Code Playgroud)

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

hash.values.reduce(:merge)
Run Code Online (Sandbox Code Playgroud)

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

  • +1我从来不知道`Symbol#to_proc`处理了arity> 1个这样的块! (3认同)
  • @tokland数组是新创建的,但数组的值仍然是原始哈希值.使用`update`是最快的(参见我的答案中的基准),但它肯定会改变原始数据中的第一个哈希值. (3认同)
  • +1这非常有趣.但它让我头疼. (2认同)
  • 注意`inject`接受一个符号:`hash.values.inject(:merge)`.使用函数`Hash#merge`绝对可以,但它会在每一步创建一个新的哈希值; 因为`hash.values`是一个新创建的时间数组,我想如果输入可能非常非常大,那么编写`hash.values.inject(:update)`就不太具有异端性. (2认同)

Phr*_*ogz 5

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

flatter = {}.tap{ |h| original.values.each{ |h2| h.merge!(h2) } }
Run Code Online (Sandbox Code Playgroud)

对当前答案的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)
Run Code Online (Sandbox Code Playgroud)

由于@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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

original.values.each{ |h| h.each{ |k,v|
  # hey, we're traversing inside!
} }
Run Code Online (Sandbox Code Playgroud)