2014-10-02 67 views

回答

3

如果:

array = [{"a"=>1},{"b"=>2},{"c"=>3},{"a"=>2},{"b"=>4}] 

,那么你可以这样做:

array.inject(Hash.new{|h,k| h[k] = 0}) 
     { |h, a| k, v = a.flatten; h[k] += v; h }. 
     map{|arr| Hash[*arr] } 
#=> [{"a"=>3}, {"b"=>6}, {"c"=>3}] 

或:

array.each_with_object(Hash.new{|h,k| h[k] = 0}) 
     { |a, h| k, v = a.flatten; h[k] += v }. 
     map{|arr| Hash[*arr] } 
#=> [{"a"=>3}, {"b"=>6}, {"c"=>3}] 
+1

'#each_with_object'就是我要找如果使用或不使用任何一个来完成。 :-)但是你做到了,所以你节省了我的时间..:P +1 – 2014-10-02 08:05:45

+1

1.你不需要'to_a'。 (为什么?)。 2.你不需要'; h'与'each_with_object'(这就是@Arup建议后者的原因)。 3.“arr”是一个合适的名字吗?该格式大大改善。 – 2014-10-02 15:26:44

+0

@CarySwoveland是的正是..这就是为什么我不使用'#注入',如果我不被迫.. .. P – 2014-10-02 15:33:21

1

有很多方法可以做到这一点。看到一些甚至是一些可能不寻常和/或不是特别有效的东西是有益的。

这里是另一种方式:

arr = [{"a"=>1},{"b"=>2},{"c"=>3},{"a"=>2},{"b"=>4}] 

arr.flat_map(&:keys) 
    .uniq 
    .map { |k| { k=>arr.reduce(0) { |t,g| t + (g.key?(k) ? g[k] : 0) } } } 
    #=> [{"a"=>3}, {"b"=>6}, {"c"=>3}] 

由于nil.to_i => 0,我们可以改为写reduce的块为:

{ |t,g| t+g[k].to_i } 
1

要在不是一比一的方式来转换集合每一次,它是#reduce工作。对于一对一转换,我们使用#map

array.reduce({}) { |h, acc| acc.merge(h) {|_k, o, n| o+n } }.zip.map(&:to_h) 
# => [{"b"=>6}, {"a"=>3}, {"c"=>3}] 

在这里,我们使用与初始值{},其被传递到块作为acc参数减少,然后我们使用#merge具有手动“解决冲突”。这意味着仅当我们试图合并的密钥已经存在于方法接收器acc中时才会调用该块。之后,我们将哈希分解为哈希数组。

+0

你需要添加'。{{| k,v | {k => v}}'或者类似于结尾的东西,将结果转换为指定的形式(散列数组)。 – 2014-10-02 06:55:49

+0

它应该产生:'[{“a”=> 3},{“b”=> 6},{“c”=> 3}]'不是'{“b”=> 6,“a”=> 3,“c”=> 3}'。我认为你需要做一些像@CarySwoveland提到的事情。 – Surya 2014-10-02 07:02:01

+0

这也是一个好方法。谢谢。 – 2014-10-02 09:50:24

2

它可以如下

array.group_by { |h| h.keys.first }. 
values. 
map {|x| x.reduce({}) { |h1, h2| h1.merge(h2) { |_, o, n| o + n } } 

#=> [{"a"=>3}, {"b"=>6}, {"c"=>3}] 
相关问题