Skip to content

Instantly share code, notes, and snippets.

@favila
Last active February 5, 2016 16:13
Show Gist options
  • Save favila/9ddda543a3f81fda237d to your computer and use it in GitHub Desktop.
Save favila/9ddda543a3f81fda237d to your computer and use it in GitHub Desktop.
grouper-by function: a reduction function compatible with `transduce` which performs an independent stepwise reduction on each group: `group-by` without the intermediate group collections!
(defn grouper-by
"Return a reducing function which reduces items in groups (determined by
result of `(keyfn item)`) independently using optional transducer
`group-xform` and reduction function `group-rf` (which should have 3 arities).
The supplied reducing and transducing functions may be stateful: their state
will be isolated to a particular group.
The returned reducing function should be run with `transduce`
or `finalizing-reduce` (not `reduce`, which does not use the \"finalize\"
arity). It will return a map keyed by `(keyfn item)` whose values are same as
`(transduce group-xform group-rf ITEMS-IN-GROUP)`.
Example use:
(transduce identity
(grouper-by first (comp (map second) (take 2)) into-set)
[[:a 1] [:b 1] [:c 1] [:a 2] [:b 2] [:a 3] [:b 3] [:d 1]])
;=> {:a #{1 2}, :b #{1 2}, :c #{1}, :d #{1}}"
([keyfn group-rf]
(grouper-by keyfn identity group-rf))
([keyfn group-xform group-rf]
(fn
([] (transient {}))
([groups] (->> (persistent! groups)
(reduce-kv
(fn [acc k a]
(let [grf (aget a 0)
gr (aget a 1)]
(assoc! acc k (grf (unreduced gr)))))
(transient {}))
(persistent!)))
([groups x]
(let [k (keyfn x)]
(if-some [gs (get groups k)]
(let [gr (aget gs 1)]
(when-not (reduced? gr)
(aset gs 1 ((aget gs 0) gr x)))
groups)
(let [grf (group-xform group-rf)
gr (group-rf)]
(assoc! groups k (doto (object-array 2)
(aset 0 grf)
(aset 1 (grf gr x)))))))))))
(defn into-set
([] (transient #{}))
([r] (persistent! r))
([r x] (conj! r x)))
(defn into-map
([] (transient {}))
([r] (persistent! r))
([r [k v]] (assoc! r k v)))
(defn finalizing-reduce
"A reduce which obeys the same 3-arity reduction function contract as
`transduce`: if `init` is not supplied, will call `(f)` for initial value;
and will call `(f result-of-reduction)` to finalize the reduction.
Same as `(transduce identity f coll)`."
([f coll] (finalizing-reduce f (f) coll))
([f init coll]
(f (reduce f init coll))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment