weavejester / medley

A lightweight library of useful Clojure functions
Eclipse Public License 1.0
865 stars 66 forks source link

filter-remove #44

Closed borkdude closed 4 years ago

borkdude commented 4 years ago

I sometimes find myself wanting to split a collection into two groups: one group for which a predicate holds and one group for which the predicate doesn't hold. You can use group-by for this which would return a map with a true and false key, but after some benchmarking, this seems to be significantly faster:

(defn filter-remove [p xs]
  (loop [xs xs
         filtered (transient [])
         removed (transient [])]
    (if xs
      (let [x (first xs)] (if (p x)
                            (recur (next xs)
                                   (conj! filtered x) removed)
                            (recur (next xs) filtered (conj! removed x))))
      [(persistent! filtered) (persistent! removed)])))

(let [[odds evens] (filter-remove odd? (range 10))] [odds evens])
;;=> [[1 3 5 7 9] [0 2 4 6 8]]

If there's enough interest for this function, I can make a PR for it.

weavejester commented 4 years ago

I'd need to consider it, but in order for filter-remove to be consistent with other functions, it would need to be lazy, and to have a transducer version.

borkdude commented 4 years ago

I don't think laziness is going to work for this function without traversing the collection twice, which is something I wanted to optimize. Never mind then.