truffle
truffle

Reputation: 555

How do I remove certain vector objects from a list in Clojure?

I have variable test in Clojure like this:

( def test '([:circle {:cx 428, :cy 245, :r 32.2490309931942, :fill red}] [circle] [:line {:x1 461, :y1 222, :x2 365, :y2 163}] [:line {:x1 407, :y1 102, :x2 377, :y2 211}] [line]))

I want to remove the [line] and [circle] objects from it and for it to look like this:

([:circle {:cx 428, :cy 245, :r 32.2490309931942, :fill red}] [:line {:x1 461, :y1 222, :x2 365, :y2 163}] [:line {:x1 407, :y1 102, :x2 377, :y2 211}] )

Is there an easy way to do this in Clojure?

I've looked at this thread How can I remove an item from a sequence in Clojure?

and remove() but I still don't have it. that thread shows:

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

but for me I have something more like:

(remove #????? ([:foo :bar] [foo] [bar]))

and I want to end up with ([:foo :bar]) only.

Upvotes: 1

Views: 470

Answers (3)

user4813927
user4813927

Reputation:

If you want to write your code just like the examples you have provided from the thread using a set of elements as the predicate for your remove (or some other functions), you only need to put the elements you want to get rid of inside of the set (just as you almost have done), but you need to be aware of symbols which need to be quoted. So the first possible error-cause in your last example is not quoting the list of your vectors:

(remove #????? ([:foo :bar] [foo] [bar])) ;; this list can not be evaluated and
;; will cause an error
(remove #????? '([:foo :bar] [foo] [bar])) ;; make it a varied list by quoting it

Now you need also to quot the symbols inside of the #{} as predicate:

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

Same rules apply also for your first example:

 (remove #{['line] ['circle]} test)
 ;;=> ([:circle {:cx 428, :cy 245, :r 32.2490309931942, :fill red}] 
 ;;    [:line {:x1 461, :y1 222, :x2 365, :y2 163}]
 ;;    [:line {:x1 407, :y1 102, :x2 377, :y2 211}]) 

will clean up your list of vectors.

Upvotes: 0

leetwinski
leetwinski

Reputation: 17859

in this exact case you would probably need something like this:

(remove (comp symbol? first) test)

output:

([:circle {:cx 428, :cy 245, :r 32.2490309931942, :fill red}] 
 [:line {:x1 461, :y1 222, :x2 365, :y2 163}] 
 [:line {:x1 407, :y1 102, :x2 377, :y2 211}])

as you want to remove all vectors whose first value is symbol.

Of course if you want to remove all the vectors with the only value which is a symbol, you should be more specific:

(remove #(and (vector? %)
              (== 1 (count %))
              (symbol? (first %)))
        test)

You could also invert your logic, not removing unneeded data, but keeping needed one:

(filter (comp keyword? first) test)

output:

([:circle {:cx 428, :cy 245, :r 32.2490309931942, :fill red}] 
 [:line {:x1 461, :y1 222, :x2 365, :y2 163}] 
 [:line {:x1 407, :y1 102, :x2 377, :y2 211}])

Upvotes: 0

Daniel Jour
Daniel Jour

Reputation: 16156

From the documentation of remove:

(remove pred) (remove pred coll)

Returns a lazy sequence of the items in coll for which (pred item) returns false.

Thus you need to provide a predicate that does so, for example to remove [circle]:

#(= '[circle] %)

This is a (anonymous) function that test whether it's argument is (value) equal to the vector [circle].

You can, of course, also generalize this to remove all one element vectors:

#(and (vector? %) (= 1 (.length %)))

Or remove every vector that contains not at least a keyword:

#(and (vector? %) (not-any? keyword? %))

I hope you get the picture :)

Upvotes: 2

Related Questions