Search code examples
algorithmclojure

Clojure Group Sequential Occurrences - Improve Function


I'm trying to group items that appear directly beside each other, so long as they are each in a given "white-list". Groupings must have at least two or more items to be included.

For example, first arg is the collection, second arg the whitelist.

(group-sequential [1 2 3 4 5] [2 3])
>> ((2 3))

(group-sequential ["The" "quick" "brown" "healthy" "fox" "jumped" "over" "the" "fence"] 
                  ["quick" "brown" "over" "fox" "jumped"])
>> (("quick" "brown") ("fox" "jumped" "over"))

(group-sequential [1 2 3 4 5 6 7] [2 3 6])
>> ((2 3))

This is what I've come up with:

(defn group-sequential
  [haystack needles]
  (loop [l haystack acc '()]
    (let [[curr more] (split-with #(some #{%} needles) l)]
      (if (< (count curr) 2)
        (if (empty? more) acc (recur (rest more) acc))
        (recur (rest more) (cons curr acc))))))

It works, but is pretty ugly. I wonder if there's a much simpler idiomatic way to do it in Clojure? (You should have seen the fn before I discovered split-with :)

I bet there's a nice one-liner with partition-by or something, but it's late and I can't quite seem to make it work.


Solution

  • (defn group-sequential [coll white] 
      (->> coll
           (map (set white))
           (partition-by nil?)
           (filter (comp first next))))
    

    ... a tidier version of Diego Basch's method.