37

First, I assume each structure-specific sequences would have different ways to remove an item: Vectors could be by index, List could be remove first or last, Set should be passing of the actual item to remove, etc.

Second, I assume there are some methods for removal that are structure agnostic; they work on seq interface.

Since sequences are immutable in Clojure, I suspect what you're actually doing is making a cheap copy of the original, only without the original item. This means list comprehension could be used for removal, but I suspect it would be unnecessarily verbose.

Please give some idiomatic examples of the different ways to remove items from Clojure sequences.

Robert Campbell
  • 6,848
  • 12
  • 63
  • 93

3 Answers3

45

There is no single interface for removing things from all of Clojure's data structure types, possibly because of the different performance characteristics.

(disj #{:foo :bar} :foo)       ; => #{:bar}
(dissoc {:foo 1 :bar 2} :foo)  ; => {:bar 2}
(pop [:bar :foo])              ; => [:bar]
(pop (list :foo :bar))         ; => (:bar)

These also work (returning a seq):

(remove #{:foo} #{:foo :bar})      ; => (:bar)
(remove #{:foo} [:foo :bar])       ; => (:bar)
(remove #{:foo} (list :foo :bar))  ; => (:bar)

This doesn't work for hash-maps because when you iterate over a map, you get key/value pairs. But this works:

(remove (fn [[k v]] (#{:foo} k)) {:foo 1 :bar 2})  ; => ([:bar 2])
Brian Carper
  • 71,150
  • 28
  • 166
  • 168
  • Thanks Brian, this is what I was looking for. Your mention of subvec doesn't seem to match the documentation: "Returns a persistent vector of the items in vector from start (inclusive) to end (exclusive). If end is not supplied, defaults to (count vector)." Did you mean that you could concat two subvec calls to leave out the "removed" item? – Robert Campbell Jun 03 '09 at 07:47
  • Yeah that's what I meant. In hindsight it may be too clumsy to even consider. I'll remove it from the post. – Brian Carper Jun 03 '09 at 17:07
  • [`subvec`](http://clojuredocs.org/clojure_core/clojure.core/subvec) is worth mentioning (for vectors), because it operates in O(1) time. – David J. Mar 14 '14 at 15:06
  • A word of caution to everyone using a primitive as a function. This fails when the items are boolean values. You have to write out the function in full: `(remove #(contains? #{false} %) [true false])`. (I did indeed run into this situation.) So strictly speaking the answer above is not correct in all cases. – m33lky Mar 03 '17 at 09:43
12

Look at the Clojure reference for sequences. filter and remove are what you seek.

Svante
  • 50,694
  • 11
  • 78
  • 122
2

As an extension of Brian Carper's answer. It depends on what you will be doing with the result. If you are passing the result to something that wants to work on the entire set of data (ie to print it) It is idiomatic to make a seq and use filter or remove to solve the problem lazily. If on the other hand you are modifying the data structure to save for various later uses then creating a seq on it would loose its favorable update characteristics so in this case its better to use the update function specific to that data structure.

Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284