如何将具有相同键的多个哈希分组到哈希数组中

时间:2015-12-02 15:08:07

标签: ruby-on-rails arrays ruby hash

从哈希数组开始:

roles =[
 {:id=>1, :name=>"alpha", :gid=>1}, 
 {:id=>2, :name=>"beta", :gid=>2},   
 {:id=>3, :name=>"delta", :gid=>1}, 
 {:id=>4, :name=>"epsilon", :gid=>1}, 
 {:id=>5, :name=>"zeta", :gid=>3}
]

我想尝试另一种结构:

groups = [
 {:gid=>1, :roles=>[
    {:id=>1, :name=>"alpha"}, 
    {:id=>3, :name=>"delta"}, 
    {:id=>4, :name=>"epsilon"}]}, 
 {:gid=>2, :roles=>[{:id=>2, :name=>"beta"}]}, 
 {:gid=>3, :roles=>[{:id=>5, :name=>"zeta"}]}
]

答案 - 所有答案输出相同..但我必须投票选出最快的答案..

time1 = Benchmark.measure do
 (1..10000).each do 
   roles.group_by { |e| e.slice(:gid) }.map{
     |k,v| k.merge(:roles => v.map { |e| e.except(:gid)}) }
 end
end
puts time1
user       system      total        real
1.150000   0.010000   1.160000 (  1.165781)

time2 = Benchmark.measure do
  (1..10000).each do
   roles.group_by{|el| el[:gid]}.map{|gid, els|
   {gid: gid, roles: els.map{ |el| { id: el[:id], name: el[:name]}}}}
   end
end
puts time2
user       system      total        real
0.270000   0.000000   0.270000 (  0.278286)

time3 = Benchmark.measure do
  (1..10000).each do
 roles.group_by{|h| h.delete(:gid)}.map{|k, v| {gid: k, roles: v}}
  end
end
puts time3
user       system      total        real
0.130000   0.000000   0.130000 (  0.134478)

4 个答案:

答案 0 :(得分:4)

您的问题的答案是:

gid = nil
h = [
  {:id=>1, :name=>"alpha", :gid=>1},
  {:id=>3, :name=>"delta", :gid=>1},
  {:id=>4, :name=>"epsilon", :gid=>1}
]
.each{|h| gid = h.delete(:gid)}
{gid: gid, roles: h}

但整个事情可以这样做:

roles.group_by{|h| h.delete(:gid)}.map{|k, v| {gid: k, roles: v}}

答案 1 :(得分:0)

roles = [
  {:id => 1, :name=>"alpha", :gid=>1}, 
  {:id => 2, :name=>"beta", :gid=>2}, 
  {:id => 3, :name=>"delta", :gid=>1}, 
  {:id => 4, :name=>"epsilon", :gid=>1},
  {:id => 5, :name=>"zeta", :gid=>3}
]

roles.group_by do |el|
  el[:gid]
end.map do |gid, els|
  { 
    gid: gid, 
    roles: els.map{ |el| { id: el[:id], name: el[:name] } } 
  }
end

答案 2 :(得分:0)

针对方法slice()except()的带有ActiveSupport的Ruby 1.8.7解决方案。

require 'rubygems'
require 'active_support/all'    

roles.group_by { |e| e.slice(:gid) }.
      map      { |k,v| k.merge(:roles => v.map { |e| e.except(:gid)}) }

#=> [{:gid=>1, :roles=>[{:name=>"alpha", :id=>1},
#                       {:name=>"delta", :id=>3},
#                       {:name=>"epsilon", :id=>4}]},
#    {:gid=>2, :roles=>[{:name=>"beta", :id=>2}]},
#    {:gid=>3, :roles=>[{:name=>"zeta", :id=>5}]}]

答案 3 :(得分:0)

这使用Hash#update(aka merge!)的形式,它使用一个块来确定合并的两个哈希中存在的键的值。这个块是:

{ |k,ov,nv| { :gid=>ov[:gid], :roles=>ov[:roles]+nv[:roles] } }

其中k是公共密钥,ov是正在构造的密钥的值,nv是要合并的哈希值。

计算:

roles.each_with_object({}) do |g,h|
  h.update(g[:gid]=>{ :gid=>g[:gid],
                      :roles=>[{:id=>g[:id], :name=>g[:name]}]
                    }
          ) { |k,ov,nv| { :gid=>ov[:gid], :roles=>ov[:roles]+nv[:roles] } }
end.values
  #=> [{:gid=>1, :roles=>[{:id=>1, :name=>"alpha"},
  #                       {:id=>3, :name=>"delta"},
  #                       {:id=>4, :name=>"epsilon"}]},
  #    {:gid=>2, :roles=>[{:id=>2, :name=>"beta"}]},
  #    {:gid=>3, :roles=>[{:id=>5, :name=>"zeta"}]}]