Hash#merge也可以接收block
Author: KDr2
13
Mar
Hash#merge被广泛用来在ruby中模拟keyword arguments,大家已经很熟练了:
- def func(given_opt)
- opt={:opt_1=>"v1",:opt_2=>"v2"}
- opt=opt.merge(given_opt)
- #blahblah...
- end
这样given_opt就能把opt里面的内容进行扩充或覆盖,rails framework里面那些help方法广泛采用了这个写法。
再考虑下面这个很平常的需求:一堆hash,要求合并,合并规则是相同key的值相加,该怎么实现呢?
- data=[{:a=>1,:b=>2},{:a=>3,:c=>4},{:b=>5,:c=>6}]
要求对data处理后结果为
一般思路为
- data=[{:a=>1,:b=>2},{:a=>3,:c=>4},{:b=>5,:c=>6}]
- rst_hash=Hash.new(0)
- data.each do |a_hash|
- a_hash.each do |k,v|
- rst_hash[k]+=v
- end
- end
倒也清晰,只是看着罗嗦,于是想能不能把merge中的覆盖效果改成其他(比如相加)效果,比如
- a={:a=>1,:b=>2}
- b={:a=>3,:c=>4}
要求a._merge_mod_(b)的结果为{:a=>4(1+3),:b=>2,:c=>4}该怎么办呢?其实只要给merge传递一个block就可以达到这个效果了:
- a.merge(b){ |key,old_value,new_value| old_value+new_value}
所以,上面那个需求就有了更简单方法了:
- data=[{:a=>1,:b=>2},{:a=>3,:c=>4},{:b=>5,:c=>6}]
- data.inject(Hash.new(0)){|t,i| t.merge(i){ |k,o,n| o+n}}
一行代码足矣
Leave a reply