group_by несколько раз в рубине - PullRequest
0 голосов
/ 21 февраля 2019

У меня есть массив хэшей, который называется 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 голосов
/ 22 февраля 2019

Вот рекурсивное решение, которое будет обрабатывать произвольные уровни вложенности и произвольные объекты группировки.

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_by , Hash # transform_values ​​ и Hash # reject .

0 голосов
/ 21 февраля 2019

Другой вариант - построить вложенную структуру при обходе хеша:

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
0 голосов
/ 21 февраля 2019

Другой вариант - группировать их, как вы делали в вопросе.Затем создайте вложенную структуру из массива, используемого в качестве ключа.

# 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.

Ссылки:

0 голосов
/ 21 февраля 2019

Я думаю, что следующее будет работать для вас,

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"}
#       ]
#     }
#   }
# }   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...