如何在一行内遍历这个哈希值?

发布于 2024-12-29 02:38:59 字数 827 浏览 4 评论 0原文

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

    {
      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(),但无法使其工作。

Each key in a hash has a value that's also a hash.

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

For each hash object, I want to discard the top-level key, and replace it with the key and value of the nested hash objects.

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

I have it working, but my method uses a merge!, and requires creating another hash to store the values. I'm curious to see if it can be done in one line. I tried to use reduce(), but could not make it work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

远山浅 2025-01-05 02:38:59

这有效:

hash.values.inject(&:merge)

编辑:另一种选择,使用reduce(与inject相同),并注意tokland的评论,即当你使用一个符号:

hash.values.reduce(:merge)

那么它不仅变得简洁而且非常可读。

This works:

hash.values.inject(&:merge)

Edit: Another option, using reduce (which is the same as inject), and noting tokland's comment that to_proc is automatically called when you use a symbol:

hash.values.reduce(:merge)

Then it becomes not only concise but very readable.

你怎么这么可爱啊 2025-01-05 02:38:59

我最喜欢 @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!
} }

I like the answer by @MarkThomas best, but for speed and memory efficiency I suggest:

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

Benchmarking 200,000 iterations of the current answers shows this to be the fastest:

                          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)

Since the comment by @tokland—original.values.reduce(:update)—modifies the original hash we cannot compare it directly to the other methods. However, if we modify all tests to put a duplicate of the first hash back into the original each iteration, @tokland's answer becomes the fastest, though still not quite as fast as mine:

                          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)

If you need absolute speed and it's OK to modify the original values, use @tokland's answer. If you do so and want to preserve the original unmerged hashes unscathed, then you can:

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

Note that your question title says traverse; if you don't really want to merge the values—if you might want to visit a duplicate key twice instead of last-in-wins—then simply do:

original.values.each{ |h| h.each{ |k,v|
  # hey, we're traversing inside!
} }
浮萍、无处依 2025-01-05 02:38:59

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

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

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

Since you don't value about the top level keys, use #values to get an array of the values (in this case, also hashes). Then you can use #inject to build up a new hash, merging them as you go.

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

There are probably other ways to do it.

抠脚大汉 2025-01-05 02:38:59
Hash[original_hash.values.flat_map(&:to_a)]
Hash[original_hash.values.flat_map(&:to_a)]
风吹短裙飘 2025-01-05 02:38:59

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

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

Just took a stab at it, first try was brute force and better methods (in both senses of the word...) are out there.

h.map {|k,v| v}.inject({}) {|i,h| i.merge(h)}
小兔几 2025-01-05 02:38:59

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

output = input.each_with_object Hash.new do |(_,subhash), hash|
  hash.merge! subhash
end

While it isn't as brief as some of the other answers, I think each_with_object deserves a representation.

output = input.each_with_object Hash.new do |(_,subhash), hash|
  hash.merge! subhash
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文