在ruby中多次group_by

时间:2019-02-21 10:46:28

标签: ruby group-by

我有一个称为events的哈希数组:

events = [
  {:name => "Event 1", :date => "2019-02-21 08:00:00", :area => "South", :micro_area => "A"},
  {:name => "Event 2", :date => "2019-02-21 08:00:00", :area => "South", :micro_area => "A"},
  {:name => "Event 3", :date => "2019-02-21 08:00:00", :area => "South", :micro_area => "B"},
  {:name => "Event 4", :date => "2019-02-21 08:00:00", :area => "South", :micro_area => "B"},
  {:name => "Event 5", :date => "2019-02-21 08:00:00", :area => "North", :micro_area => "A"},
  {:name => "Event 6", :date => "2019-02-21 08:00:00", :area => "North", :micro_area => "A"},
  {:name => "Event 7", :date => "2019-02-21 08:00:00", :area => "North", :micro_area => "B"},
  {:name => "Event 8", :date => "2019-02-21 08:00:00", :area => "North", :micro_area => "B"}
]

我想知道如何首先group_by,然后date,然后area micro_area,最后以单个散列数组结束,例如:

[
  {
    "2019-02-21 08:00:00": {
      "South": {
        "A": [
          {:name=>"Event 1", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"A" },
          {:name=>"Event 2", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"A" }
        ],
        "B": [
          {:name=>"Event 3", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"B" },
          {:name=>"Event 4", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"B" }
        ]  
      },
      "North": {
        "A": [
          {:name=>"Event 5", :date=>"2019-02-21 08:00:00", :area=>"North", :micro_area=>"A" },
          {:name=>"Event 6", :date=>"2019-02-21 08:00:00", :area=>"North", :micro_area=>"A" }
        ],
        "B": [
          {:name=>"Event 7", :date=>"2019-02-21 08:00:00", :area=>"North", :micro_area=>"B" },
          {:name=>"Event 8", :date=>"2019-02-21 08:00:00", :area=>"North", :micro_area=>"B" }
        ]  
      }
    }
  }
] 

尝试events.group_by { |r| [r[:date], r[:area], r[:micro_area]] }似乎不太符合我的期望。

4 个答案:

答案 0 :(得分:7)

我认为,以下内容将为您服务

events = [
  { name: 'Event 1', date: '2019-02-21 08:00:00', area: 'South', micro_area: 'A' }
]

events.group_by { |x| x[:date] }.transform_values do |v1|
  v1.group_by { |y| y[:area] }.transform_values do |v2|
    v2.group_by { |z| z[:micro_area] }
  end
end
# {
#   "2019-02-21 08:00:00"=>{
#     "South"=>{
#       "A"=>[
#         {:name=>"Event 1", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"A"}
#       ]
#     }
#   }
# }   

答案 1 :(得分:4)

另一种选择是遍历哈希时构建嵌套结构:

events.each_with_object({}) do |event, result|
  d, a, m = event.values_at(:date, :area, :micro_area)
  result[d] ||= {}
  result[d][a] ||= {}
  result[d][a][m] ||= []
  result[d][a][m] << event
end

答案 2 :(得分:3)

另一个选择是像在问题中一样对它们进行分组。然后从用作键的数组构建嵌套结构。

# build an endless nested structure
nested = Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) }

# group by the different criteria and place them in the above nested structure
events.group_by { |event| event.values_at(:date, :area, :micro_area) }
      .each { |(*path, last), events| nested.dig(*path)[last] = events }

# optional - reset all default procs
reset_default_proc = ->(hash) { hash.each_value(&reset_default_proc).default = nil if hash.is_a?(Hash) }
reset_default_proc.call(nested)

以上内容将答案留在nested变量中。

参考:

答案 3 :(得分:0)

这是一种递归解决方案,它将处理任意级别的嵌套和任意分组对象。

def hashify(events, grouping_keys)
  return events if grouping_keys.empty?
  first_key, *remaining_keys = grouping_keys
  events.group_by { |h| h[first_key] }.
         transform_values { |a|
           hashify(a.map { |h|
             h.reject { |k,_| k == first_key } },
             remaining_keys) }
end

在使用来自问题的示例数据执行此操作之前,我们将一个具有不同日期的哈希添加到events

events <<
  { :name=>"Event 8", :date=>"2018-12-31 08:00:00",
    :area=>"North",   :micro_area=>"B" }

grouping_keys = [:date, :area, :micro_area]

hashify(events, grouping_keys)
  #=> {"2019-02-21 08:00:00"=>{
  #      "South"=>{
  #        "A"=>[{:name=>"Event 1"}, {:name=>"Event 2"}],
  #        "B"=>[{:name=>"Event 3"}, {:name=>"Event 4"}]
  #      },
  #      "North"=>{
  #        "A"=>[{:name=>"Event 5"}, {:name=>"Event 6"}],
  #        "B"=>[{:name=>"Event 7"}, {:name=>"Event 8"}]
  #      }
  #    },
  #    "2018-12-31 08:00:00"=>{
  #      "North"=>{
  #        "B"=>[{:name=>"Event 8"}]
  #      }
  #    }
  #  } 

hashify(events, [:date, :area])
  #=> {"2019-02-21 08:00:00"=>{
  #      "South"=>[
  #        {:name=>"Event 1", :micro_area=>"A"},
  #        {:name=>"Event 2", :micro_area=>"A"},
  #        {:name=>"Event 3", :micro_area=>"B"},
  #        {:name=>"Event 4", :micro_area=>"B"}
  #      ],
  #      "North"=>[
  #        {:name=>"Event 5", :micro_area=>"A"},
  #        {:name=>"Event 6", :micro_area=>"A"},
  #        {:name=>"Event 7", :micro_area=>"B"},
  #        {:name=>"Event 8", :micro_area=>"B"}
  #      ]
  #    },
  #    "2018-12-31 08:00:00"=>{
  #      "North"=>[
  #       {:name=>"Event 8", :micro_area=>"B"}
  #      ]
  #    }
  #  } 

请参见Enumerable#group_byHash#transform_valuesHash#reject