Welcome! Please see the About page for a little more info on how this works.

+5 votes
in Sequences by

Is there some existing core function that can be used to partition a sorted list so that [1 2 3 7 8 14 15 16 17 20 21 22] becomes [[1 2 3] [7 8] [14 15 16 17] [20 21 22]]?

I can think of how to do with with a loop (start a new list each time the difference between current and previous is greater than 1), but I suspect there's a functional way of expressing this.

For context, this toy problem is a simplified version of a data cleaning exercise where I'm trying to identify where a sensor's data collection was interrupted (e.g. power outage) by examining the difference between the timestamps of the data. I can see functions like split-with, but I don't think the predicate provided to those functions can be provided with the value of the previous item in the sequence in order to make the comparison between items in the sequence.

Thanks for your consideration :)

2 Answers

+4 votes
selected by
Best answer

As far as I know, there is no such partition-when that does partitioning when predicate returns true, which is needed in your case.
I would suggest using reduce as an intermediate level between high-level functional processing and low-level loop — it is simpler in a sense that the processing is bound to a number of elements in your coll:

  (fn [acc n]
    (let [prev (peek (peek acc))]
      (if (and prev (= -1 (- prev n)))
        (update acc (dec (count acc)) conj n)
        (conj acc [n]))))
  [1 2 3 7 8 14 15 16 17 20 21 22])
Thanks, I suppose I'm still not used to using reduce to build up collections, but in hindsight it makes sense that reduce is the basic tool to deal with this kind of problem without dropping down to a loop.
+2 votes

The library dev.weavejester/medley ("A small collection of useful, mostly pure functions that might not look out of place in the clojure.core namespace") contains partition-between since 1.7.0. That function can be used to partition a sorted list:

(require '[medley.core :refer [partition-between]])

(partition-between (fn [x y] (> y (inc x))) [1 2 3 7 8 14 15 16 17 20 21 22])
;; => ((1 2 3) (7 8) (14 15 16 17) (20 21 22))