1

给定一个像这样的 Ruby 哈希数组:

[{"lib1"=>"30"}, {"lib2"=>"30"}, {"lib9"=>"31"}, {"lib2"=>"31"}, {"lib3"=>"31"}, {"lib1"=>"32"}, {"lib2"=>"32"}, {"lib1"=>"33"}, {"lib3"=>"36"}, {"lib2"=>"36"}, {"lib1"=>"37"}]

我如何得到这样的哈希:

{"lib1"=>[30,32,33,37], lib2=>[30,31,32,36], lib3=>[31,36], lib9=>[31]}
4

5 回答 5

3
a = [{"lib1"=>"30"}, {"lib2"=>"30"}, {"lib9"=>"31"}, {"lib2"=>"31"}, {"lib3"=>"31"}, {"lib1"=>"32"}, {"lib2"=>"32"}, {"lib1"=>"33"}, {"lib3"=>"36"}, {"lib2"=>"36"}, {"lib1"=>"37"}]

a.map(&:to_a).flatten(1).each_with_object({}) do |(k, v), h|
  h[k] ||= []
  h[k] << v
end
#=> {"lib1"=>["30", "32", "33", "37"],
#    "lib2"=>["30", "31", "32", "36"],
#    "lib9"=>["31"],
#    "lib3"=>["31", "36"]}

或者:

Hash[a.map(&:to_a).flatten(1).group_by(&:first).map { |k, v| [k, v.map(&:last)] }]

如果您愿意使用Facets,那么这将变得非常简单collate

a.inject(:collate)
于 2012-10-21T22:01:09.077 回答
2
t = [{"lib1"=>"30"}, {"lib2"=>"30"}, {"lib9"=>"31"}, {"lib2"=>"31"},
  {"lib3"=>"31"}, {"lib1"=>"32"}, {"lib2"=>"32"}, {"lib1"=>"33"},
  {"lib3"=>"36"}, {"lib2"=>"36"}, {"lib1"=>"37"}]
result = {}

t.group_by { |x| x.keys.first }.each_pair do |k, v|
  result[k] = v.map { |e| e.values.first }
end

或者,对于一个更纯粹的功能版本和合适的,在最重要的一行上(毕竟是 Ruby)......

Hash[t.group_by { |x| x.keys[0] }.map { |k, v| [k, v.map { |e| e.values[0] }]}]
于 2012-10-21T22:28:37.840 回答
2

替代 Andrew's,避免 flatten 和 to_a,只是嵌套迭代。如果存在,将从源数组中的元素哈希中收集多个键。

a.each_with_object({}) do |element,result|
  element.each do |k,v|
    (result[k] ||= []) << v.to_i
  end
end

打到单线:

a.each_with_object({}) {|e,r| e.each {|k,v| (r[k] ||= []) << v.to_i } }

我会注意到这个版本只检查每个源元素一次,而 to_a/flatten 和 group_by 答案涉及对源的多次迭代或源的转换。

Andrew 提出了一个很好的观点,即 big-O 算法复杂性中的常数因素在现实中通常是一种洗牌。我对迄今为止提供的答案进行了快速基准测试(如 OP 示例所暗示的那样,将它们全部纠正为将值转换为 fixnum)。使用 OP 示例源数据,我的嵌套迭代方法确实更快(23-45%):

ruby 1.9.2p318 (2012-02-14 revision 34678) [x86_64-linux]
Rehearsal ------------------------------------------------------------
to_a_flat                  3.100000   0.000000   3.100000 (  3.105873)
to_a_flat_construct        4.060000   0.000000   4.060000 (  4.076938)
group_by_each              3.010000   0.000000   3.010000 (  3.015367)
group_by_each_construct    3.040000   0.000000   3.040000 (  3.050500)
nested_iter                2.300000   0.000000   2.300000 (  2.307776)
-------------------------------------------------- total: 15.510000sec

                               user     system      total        real
to_a_flat                  3.080000   0.000000   3.080000 (  3.096301)
to_a_flat_construct        4.050000   0.000000   4.050000 (  4.059409)
group_by_each              2.980000   0.000000   2.980000 (  2.997074)
group_by_each_construct    3.050000   0.000000   3.050000 (  3.057770)
nested_iter                2.300000   0.000000   2.300000 (  2.311855)
于 2012-10-21T22:37:29.563 回答
0

只是为了增加频谱;您可以通过预先定义生成的 Hash 并使用常规的 .each 循环来避免 each_with_object。我发现它比前一种方法更容易阅读。

grouped = {}
hashes.each do |hash|
  hash.each do |key, value|
    (grouped[key] ||= []) << value.to_i
  end
end
于 2014-02-25T16:02:56.027 回答
-1

类似于 DigitalRoss 的,但我会在原地做:

array.group_by{|h| h.keys.first}.each{|_, a| a.map!{|h| h.values.first}}
于 2012-10-21T22:56:18.060 回答