Обобщение group-by
Мне нужно было обобщение group-by
, которое дало бы более 2-х вложенных карт-карт.Я хотел иметь возможность дать такой функции список произвольных функций для рекурсивного запуска через group-by
.Вот что я придумал:
(defn map-function-on-map-vals
"Take a map and apply a function on its values. From [1].
[1] http://stackoverflow.com/a/1677069/500207"
[m f]
(zipmap (keys m) (map f (vals m))))
(defn nested-group-by
"Like group-by but instead of a single function, this is given a list or vec
of functions to apply recursively via group-by. An optional `final` argument
(defaults to identity) may be given to run on the vector result of the final
group-by."
[fs coll & [final-fn]]
(if (empty? fs)
((or final-fn identity) coll)
(map-function-on-map-vals (group-by (first fs) coll)
#(nested-group-by (rest fs) % final-fn))))
Ваш пример
Применительно к вашему набору данных:
cljs.user=> (def foo [ ["A" 2011 "Dan"]
#_=> ["A" 2011 "Jon"]
#_=> ["A" 2010 "Tim"]
#_=> ["B" 2009 "Tom"] ])
cljs.user=> (require '[cljs.pprint :refer [pprint]])
nil
cljs.user=> (pprint (nested-group-by [first second] foo))
{"A"
{2011 [["A" 2011 "Dan"] ["A" 2011 "Jon"]], 2010 [["A" 2010 "Tim"]]},
"B" {2009 [["B" 2009 "Tom"]]}}
Создает точно желаемый результат.nested-group-by
может принимать три или четыре или более функций и создает такое количество вложений хэш-карт.Возможно, это будет полезно для других.
Удобная функция
nested-group-by
также имеет удобную дополнительную функцию: final-fn
, которая по умолчанию равна identity
, поэтому, если вы ее не предоставитесамое глубокое вложение возвращает вектор значений, но если вы укажете final-fn
, он будет выполняться для самых внутренних векторов.Для иллюстрации: если вы просто хотели узнать, сколько строк исходного набора данных появилось в каждой категории и году:
cljs.user=> (nested-group-by [first second] foo count)
#^^^^^ this is final-fn
{"A" {2011 2, 2010 1}, "B" {2009 1}}
Предупреждение
Эта функция не использует recur
так глубоко-рекурсивные вызовы могут взорвать стек.Однако для ожидаемого варианта использования с небольшим набором функций это не должно быть проблемой.