Fork me on GitHub
#clojure
<
2022-07-09
>
olaf00:07:17

Hi! How can I group by two all elements of a list of lists?

((1) (2) (3) (4) (5)) ;=> ((1 2) (3 4) (5)) 

phronmophobic00:07:20

in general, a good place to start is to look through the seq library, https://clojure.org/reference/sequences#_seq_in_seq_out

👍 1
TW09:07:14

(partition-all 2 (flatten '((1) (2) (3) (4) (5)))) ;=> ((1 2) (3 4) (5))

hiredman00:07:59

Partition by 2, map apply concat

🙌 1
seancorfield00:07:52

(hint: partition drops any extra elements, partition-all preserves them)

👍 1
olaf00:07:49

What if instead of partition by 2 I use a given condition? e.g. #(split-here? %) ;=> true/false

((1) (2) (3) (4) (5) (6) (7)) ;=> ((1 2) (3) (4) (5 6 7)) 

olaf01:07:15

I’ve tried partition-by but groups items together. I’ve a collection of has-map ordered and I’ve to split them when a value of an item is null.

(partition-by #(nil? (:key %)) coll)
is not what I expect

Drew Verlee02:07:09

what do you want ?

(partition-by #(-> % :k nil?)
   [{:k 1} {:k 2} {:k nil} {:k 3} {:k 4}])

 (({:k 1} {:k 2}) ({:k nil}) ({:k 3} {:k 4})) 
sounds like what you want

olaf12:07:24

I was looking more for

[{:k 1} {:k 2} {:k nil} {:k 3} {:k 4}]

(({:k 1} {:k 2}) ({:k nil} {:k 3} {:k 4})) 
something like (split-when f coll)

olaf13:07:02

(defn split-when [f coll]
  (loop [xs coll
         acc [[]]]
    (if-let [x (first xs)]
      (if (f x)
        (recur (rest xs)
               (conj acc [x]))
        (recur (rest xs)
               (conj (pop acc) (conj (peek acc) x))))
      acc)))
not sure if there’s a better way to do it

Ed12:07:03

you could always define it as a transducer to avoid consuming the whole collection to decide when to split. Maybe something like:

(defn split-when [f]
  (fn [rf]
    (let [col (volatile! [])]
      (fn
        ([] (rf))
        ([r]
         (rf (if (seq @col)
               (rf r @col)
               r)))
        ([r x]
         (if (f x)
           (let [v (rf r @col)]
             (vreset! col [x])
             v)
           (do (vswap! col conj x)
               r)))))))

(comment

  (sequence (comp (map inc)
                  (split-when #{5}))
            (range 10)) ;=> ([1 2 3 4] [5 6 7 8 9 10])
  
  )
??

zimablue10:07:18

in the clojure definition of macroexpand, why does it use an equality check as the termination condition for the recursion rather than directly checking whether the first element is a macro? is it because in order to check whether something is a macro you have to resolve it, which is somehow limiting?

bronsa17:07:02

because an identical? check is very fast, and the impl is easier to write like that