4clojure 63 Group a Sequence

3 Jun 2012

My first attempt at writing up one of my 4clojure solutions. Problem 63 “Group a Sequence” requires you to re-implement group-by. As always looking at the source code is cheating :-)

Given a function f and a sequence s, write a function which returns a map. The keys should be the values of f applied to each item in s. The value at each key should be a vector of corresponding items in the order they appear in s.

(= (__ #(> % 5) [1 3 6 8]) {false [1 3], true [6 8]})

Here’s how I worked it out in the REPL, warts and all…

;; map seems a good start here

((map #(> % 5) [1 3 6 8])
;; (false false true true)

;; so I’m getting the values of fn here, I want the inputs too ;; I can’t think of a clever way to do this, I’ll just use for and list

(defn foo [f xs] (for [x xs :let [y (f x)]] (list y x)))
(foo #(> % 5) [1 3 6 8])
;; ((false 1) (false 3) (true 6) (true 8))

;; the for loop can be tightend up, I don’t need the :let

(defn foo [f xs] (for [x xs] (list (f x) x)))
(foo #(> % 5) [1 3 6 8])
;; ((false 1) (false 3) (true 6) (true 8))

;; I know I want my results as maps, so use a map instead

(defn foo [f xs] (for [x xs] {(f x) x}))
(foo #(> % 5) [1 3 6 8])
;; ({false 1} {false 3} {true 6} {true 8})

;; now I’m stuck; how do I moosh the results together? ;; I read thru all the map change fn’s in the clj cheatsheet ;; hmmm, merge-with looks promising

(defn foo [f xs] (merge-with concat (for [x xs] {(f x) x})))
(foo #(> % 5) [1 3 6 8])
;; ({false 1} {false 3} {true 6} {true 8})

;; WTF? same results. Re-RTFM on merge-with. Well, I’m going to ;; need vectors in my results to be able to concat them…

(defn foo [f xs] (merge-with concat (for [x xs] {(f x) [x]})))
(foo #(> % 5) [1 3 6 8])
;; ({false [1]} {false [3]} {true [6]} {true [8]})

;; WTF? still not mooshing together. Uh duh, fn signature takes ;; “maps” not a list. Use the “list stripper” - apply!

(defn foo [f xs] (apply merge-with concat (for [x xs] {(f x) [x]})))
(foo #(> % 5) [1 3 6 8])
;; {true (6 8), false (1 3)}

;; ha, sweetness and light! Remove the defn and submit:

(fn [f xs] (apply merge-with concat (for [x xs] {(f x) [x]})))

;; look at other’s solutions. Dacquiri as usual has a more elegant ;; solution, with a reader macro. What’s his golf handicap?!?!

#(apply merge-with concat (for [x %2] {(% x) [x]}))

Mooshing is an advanced programming technique, that can only be used after years of study and practice…

comments powered by Disqus

  « Previous: Next: »