我有一个对象数组,如下所示:
[
{day: 'Monday', class: 1, name: 'X'},
{day: 'Monday', class: 2, name: 'Y'},
{day: 'Tuesday', class: 1, name: 'Z'},
{day: 'Monday', class: 1, name: 'T'}
]
我希望按天分组,然后按类
分组groupedArray['Monday'] => {'1' => [{name: 'X'}, {name: 'T'}], '2' => [{name: 'Y'}]}
我见过有一个
group_by { |a| [a.day, a.class]}
但是这会创建一个带[day,class]键的哈希值。
有没有办法可以实现这一目标,而不必先将它们分组,然后每天迭代,然后按类分组,然后将它们推入新的哈希值?
答案 0 :(得分:2)
arr = [
{day: 'Monday', class: 1, name: 'X'},
{day: 'Monday', class: 2, name: 'Y'},
{day: 'Tuesday', class: 1, name: 'Z'},
{day: 'Monday', class: 1, name: 'T'}
]
获得所需散列的一种方法是使用Hash#update(也称为merge!)的形式,该形式使用块来确定合并的两个散列中存在的键的值。这里做了两次,首先当:day
的值相同时,然后对于每次这样的事件,当:class
的值相同时(对于给定的:day
值)。
arr.each_with_object({}) { |g,h|
h.update(g[:day]=>{ g[:class].to_s=>[{name: g[:name] }] }) { |_,h1,h2|
h1.update(h2) { |_,p,q| p+q } } }
#=> {"Monday" =>{"1"=>[{:name=>"X"}, {:name=>"T"}], "2"=>[{:name=>"Y"}]},
# "Tuesday"=>{"1"=>[{:name=>"Z"}]}}
步骤如下。
enum = arr.each_with_object({})
#=> #<Enumerator: [{:day=>"Monday", :class=>1, :name=>"X"},
# {:day=>"Monday", :class=>2, :name=>"Y"},
# {:day=>"Tuesday", :class=>1, :name=>"Z"},
# {:day=>"Monday", :class=>1, :name=>"T"}]:each_with_object({})>
我们可以看到这个枚举器通过将它转换为数组而生成的值:
enum.to_a
#=> [[{:day=>"Monday", :class=>1, :name=>"X"}, {}],
# [{:day=>"Monday", :class=>2, :name=>"Y"}, {}],
# [{:day=>"Tuesday", :class=>1, :name=>"Z"}, {}],
# [{:day=>"Monday", :class=>1, :name=>"T"}, {}]]
每个数组中的空哈希是正在构建和返回的哈希。它最初是空的,但会在处理enum
的每个元素时部分形成。
enum
的第一个元素传递给块(Enumerator#each),块变量使用并行赋值分配(somtimes称为多重赋值< / em>的):
g,h = enum.next
#=> [{:day=>"Monday", :class=>1, :name=>"X"}, {}]
g #=> {:day=>"Monday", :class=>1, :name=>"X"}
h #=> {}
我们现在执行块计算:
h.update(g[:day]=>{ g[:class].to_s=>[{name: g[:name] }] })
#=> {}.update("Monday"=>{ "1"=>[{name: "X"}] })
#=> {"Monday"=>{"1"=>[{:name=>"X"}]}}
此操作返回h
的更新值,即正在构造的哈希值。
请注意update
的参数
"Monday"=>{ "1"=>[{name: "X"}] }
是
的简写{ "Monday"=>{ "1"=>[{name: "X"}] } }
因为合并的两个哈希中都没有键"Monday"
(h
没有键),块
{ |_,h1,h2| h1.update(h2) { |_,p,q| p+q } } }
未用于确定"Monday"
的值。
现在将enum
的下一个值传递给块并分配块变量:
g,h = enum.next
#=> [{:day=>"Monday", :class=>2, :name=>"Y"},
# {"Monday"=>{"1"=>[{:name=>"X"}]}}]
g #=> {:day=>"Monday", :class=>2, :name=>"Y"}
h #=> {"Monday"=>{"1"=>[{:name=>"X"}]}}
请注意h
已更新。我们现在执行块计算:
h.update(g[:day]=>{ g[:class].to_s=>[{name: g[:name] }] })
# {"Monday"=>{"1"=>[{:name=>"X"}]}}.update("Monday"=>{ "2"=>[{name: "Y"}] })
两个哈希合并共享密钥&#34;星期一&#34;。因此,我们必须使用该块来确定&#34; Monday&#34;的合并值:
{ |k,h1,h2| h1.update(h2) { |m,p,q| p+q } } }
#=> {"1"=>[{:name=>"X"}]}.update("2"=>[{name: "Y"}])
#=> {"1"=>[{:name=>"X"}], "2"=>[{:name=>"Y"}]}
请参阅update
的文档,了解外部k
和h1
的块变量h2
,update
和m
的说明内部p
,q
和update
。 k
和m
是公钥的值。因为它们没有用于块计算中,所以我用下划线替换它们,这是常见的做法。
现在:
h #=> { "Monday" => { "1"=>[{ :name=>"X" }], "2"=>[{ :name=>"Y"}] } }
在此操作之前,哈希h["Monday]
还没有密钥2
,因此第二个update
不需要使用块
{ |_,p,q| p+q }
但是,当enum
的最后一个元素合并到h
时,会使用此块,因为:day
和:class
的值都相同两个哈希合并。
其余的计算方法类似。