Skip to content

Instantly share code, notes, and snippets.

@alexpw
Created December 20, 2012 16:23
Show Gist options
  • Save alexpw/4346395 to your computer and use it in GitHub Desktop.
Save alexpw/4346395 to your computer and use it in GitHub Desktop.
Seq utilities for grouping and indexing.
(defn group-by-with
"A group-by factory that accepts a combiner-fn to control how the values
of the grouping fn are collected."
[combiner-fn]
(fn [f coll]
(persistent!
(reduce
(fn [ret x]
(let [k (f x)]
(assoc! ret k (combiner-fn (get ret k) x))))
(transient {}) coll))))
;; A modified group-by that does not create a vector of grouped elements;
;; instead, only one element can be the mapped value.
(def group-by-one (group-by-with (fn [xs x] x)))
(def group-by-set (group-by-with (fn [xs x]
(if (nil? xs)
(set x)
(conj xs x)))))
(defn index-by-with
"A index-by factory that accepts a combiner-fn to control how the values
of the grouping fn are collected."
[combiner-fn]
(fn self
([coll f] (combiner-fn f coll))
([coll f & fs]
(persistent!
(reduce
(fn [ret [k els]]
(assoc! ret k (apply self els fs)))
(transient {})
(group-by f coll))))))
;; Returns a nested hash map of the elements of coll keyed by the result of
;; any number of functions on the elements. The leaves will be a vector of
;; elements from the coll, just like a regular group-by.
(def index-by (index-by-with group-by))
;; Returns a nested hash map of the elements of coll keyed by the result of
;; any number of functions on the elements, to create a nested hash map
;; where the leaves are a single value, instead of a vector, like group-by.
;; Warning, assumes that the set of supplied functions will reduce the
;; coll down to a single element; otherwise, the value will be last element.
(def index-by-unique (index-by-with group-by-one))
;; Same as above but the leaves are combined into a set.
(def index-by-set (index-by-with group-by-set))
;; Examples
(comment
(facts
(index-by-unique [{:a 1} {:a 2}] :a) => {1 {:a 1}
2 {:a 2}}
(index-by-unique [{:a 1 :b 2} {:a 2 :b 1}] :a) => {1 {:a 1 :b 2}
2 {:a 2 :b 1}}
(index-by-unique [{:a 1 :b 2} {:a 2 :b 1}] :a :b) => {1 {2 {:a 1 :b 2}}
2 {1 {:a 2 :b 1}}})
(facts
(index-by [{:a 1} {:a 2}] :a) => {1 [{:a 1}]
2 [{:a 2}]}
(index-by [{:a 1 :b 2} {:a 2 :b 1}] :a) => {1 [{:a 1 :b 2}]
2 [{:a 2 :b 1}]}
(index-by [{:a 1 :b 2} {:a 2 :b 1}] :a :b) => {1 {2 [{:a 1 :b 2}]}
2 {1 [{:a 2 :b 1}]}}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment