Ruby哈希数组按name分组合并data哈希的正确方法
问题:按Name分组合并哈希数组中的Data字段
我现在需要处理一个Ruby哈希数组,通过这段代码生成了初始数据:
hash = Modality.joins(:exam).where(exams: {physician_id: physician}).map {|m| {name: m.name, data: {m.exam.from_date => m.total}} }
初始输出是这样的:
[ {:name=>"MR", :data=>{Sun, 01 Jan 2017=>44}}, {:name=>"CT", :data=>{Sun, 01 Jan 2017=>7}}, {:name=>"US", :data=>{Sun, 01 Jan 2017=>1}}, {:name=>"MR", :data=>{Wed, 01 Feb 2017=>41}}, {:name=>"CT", :data=>{Wed, 01 Feb 2017=>4}}, {:name=>"MR", :data=>{Wed, 01 Mar 2017=>66}}, {:name=>"CT", :data=>{Wed, 01 Mar 2017=>6}}, {:name=>"XR", :data=>{Wed, 01 Mar 2017=>1}}, {:name=>"CT", :data=>{Sat, 01 Apr 2017=>8}}, {:name=>"US", :data=>{Sat, 01 Apr 2017=>1}}, {:name=>"MR", :data=>{Sat, 01 Apr 2017=>73}} ]
我的目标是按:name键分组,把每个分组里的:data哈希合并起来,得到这样的结果:
[ {:name=>"MR", :data=>{Sun, 01 Jan 2017=>44, Wed, 01 Feb 2017=>41, Wed, 01 Mar 2017=>66, Sat, 01 Apr 2017=>73}}, {:name=>"CT", :data=>{Sun, 01 Jan 2017=>7, Wed, 01 Feb 2017=>4, Wed, 01 Mar 2017=>6, Sat, 01 Apr 2017=>8}}, {:name=>"US", :data=>{Sun, 01 Jan 2017=>1, Sat, 01 Apr 2017=>1}}, {:name=>"XR", :data=>{Wed, 01 Mar 2017=>1}} ]
我试过这段代码:
exams = hash.group_by {|d| d[:name]}.map {|k, v| v.reduce(:merge)}
但结果不对,只保留了每个分组里第一个元素的:data:
[ {:name=>"MR", :data=>{Sun, 01 Jan 2017=>44}}, {:name=>"CT", :data=>{Sun, 01 Jan 2017=>7}}, {:name=>"US", :data=>{Sun, 01 Jan 2017=>1}}, {:name=>"XR", :data=>{Wed, 01 Mar 2017=>1}} ]
请问怎么才能正确实现这种哈希合并?
解决方案
问题出在v.reduce(:merge)上:默认的Hash#merge会直接覆盖相同键的值,当你合并整个元素哈希时,后面元素的:data会直接替换前面的,而不是合并内部的键值对。我们需要单独处理:data的合并,而不是合并整个元素哈希。
方法一:用each_with_object合并数据
exams = hash.group_by { |d| d[:name] }.map do |name, entries| combined_data = entries.each_with_object({}) do |entry, acc| acc.merge!(entry[:data]) end { name: name, data: combined_data } end
方法二:用reduce简洁实现
exams = hash.group_by { |d| d[:name] }.map do |name, entries| { name: name, data: entries.reduce({}) { |acc, entry| acc.merge(entry[:data]) } } end
方法三:Ruby 2.6+ 用transform_values简化
如果你的Ruby版本是2.6及以上,可以用transform_values让代码更简洁:
exams = hash.group_by { |d| d[:name] }.transform_values do |entries| entries.reduce({}) { |acc, e| acc.merge(e[:data]) } end.map { |name, data| { name: name, data: data } }
这几种方法都会把每个分组下的所有:data哈希合并成一个完整的哈希,最终得到你想要的分组结果。
内容的提问来源于stack exchange,提问作者bradbajuz
相关产品推荐
相关产品推荐

