grouping and mapping

298 views
Skip to first unread message

Erik Assum

unread,
Aug 12, 2016, 1:10:37 PM8/12/16
to clo...@googlegroups.com
I’ve been working on a new project in java 8 at a new client where I’ve been able to play with streams.
In doing so I’ve come across Collectors.groupingBy which has some additional features that group-by doesn’t seem to have.

Example:
Given
(def foos [[:a "foo"] [:b "bar"] [:a "baz"]])

(group-by first foos)
;=> {:a [[:a "foo"] [:a "baz"]], :b [[:b "bar"]]}

but I’d like the result to be
;=> {:a ["FOO" "BAZ"] :b ["BAR”]}

This is solved in javas stream api by something like this:

List<List<String>> = [["a", "foo"], ["b" "bar"], ["a", "baz"]];

foos
.stream()
.collect(Collectors.groupingBy(e -> e.get(0),
Collectors.mapping(e -> e.get(1).toUpperCase(),
Collectors.toList())));

Where the key point is that Collectors.groupingBy accepts a mapping function in which to map the values being grouped.

So how would one go about writing this in Clojure? I could of-course write something that mapped over the grouped map,
but I’d really like to write something like

(defn mapping-group-by grouping-fn mapping-fn coll)

but I have a suspicion that this already exists in some form already?

Erik.

Moe Aboulkheir

unread,
Aug 12, 2016, 1:14:19 PM8/12/16
to clo...@googlegroups.com
As far as already existing, (grouped-map first (comp str/upper-case second) ...) or similar, with https://github.com/plumatic/plumbing/blob/master/src/plumbing/core.cljx#L164

Take care,
Moe

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Christophe Grand

unread,
Aug 12, 2016, 5:43:22 PM8/12/16
to clojure
I have a collection of transducers that I use in such cases:

(require '[net.cgrand.xforms :as x]
  '[clojure.string :as str])

(into {} (x/by-key (comp (map str/upper-case) (x/into [])))
;=> [[:a "foo"]  [:b "bar"]  [:a "baz"]])


--
On Clojure http://clj-me.cgrand.net/
Clojure Programming http://clojurebook.com
Training, Consulting & Contracting http://lambdanext.eu/

Simon Belak

unread,
Aug 13, 2016, 10:47:17 AM8/13/16
to Clojure
Another option (and shameless self promotion) is the rollup family of functions in https://github.com/sbelak/huri

Your case would be:

(rollup first identity second foos)
=> {:a ("foo" "baz"), :b ("bar")}

Nathan Smutz

unread,
Aug 14, 2016, 12:27:52 PM8/14/16
to Clojure
Just to put the basic Clojure out there:

(->> (group-by first foos)
(map (fn [[key col]]
[key (mapv (comp clojure.string/upper-case second)
col)]))
(into {})

Kudos to Moe,Christopher and Simon.

miner

unread,
Aug 14, 2016, 2:53:09 PM8/14/16
to Clojure
If your data elements are essentially map-entries (key-value pairs), I'd use reduce.  Maybe something like this would work for you...

(defn mapping-group-by [grouping-fn mapping-fn coll]
  (reduce (fn [res [k v]] (update res k grouping-fn (mapping-fn v)))
          {}
          coll))

(def foos [[:a "foo"]  [:b "bar"]  [:a "baz"]])

(mapping-group-by conj clojure.string/upper-case foos)
;;=> {:a ("BAZ" "FOO"), :b ("BAR")}

If you need vector values, you can wrap `conj` with `fnil` to get a vector.

(mapping-group-by (fnil conj []) clojure.string/upper-case foos)
;;=> {:a ["FOO" "BAZ"] :b ["BAR"]}

Steve Miner

unread,
Aug 14, 2016, 2:57:42 PM8/14/16
to clo...@googlegroups.com
Sorry, I got the semantics of the grouping-fn wrong.  My code assumes that the key is always first in the elements and my grouping is more like a merging-fn.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages