Home Ask Login Register

Developers Planet

Your answer is one click away!

JT93 February 2016

Using disj to remove an element from a set in Clojure

Hi I've hit a brick wall whilst trying to remove an element from a set.

I have a map of cards.

   (def cards
    :card1 {:name "Wisp"              :type "Monster"     :damage 1 :health 1 :cost 0 :charge "t"}
    :card2 {:name "Spider Tank"       :type "Monster"     :damage 3 :health 4 :cost 3}
    :card3 {:name "Boulder Fist Ogre" :type "Monster"     :damage 6 :health 7 :cost 6}


And a deck (set) of these cards.

(def deck1 (set (map cards '(:card1 :card2 :card3))))

When I use disj to try and remove one of these cards nothing happens.

(disj deck1 :card1)

I really have no idea why.


JT93 February 2016

leetwinski was right in the comments. (disj deck1 (:card1 cards)) is correct.

jmargolisvt February 2016

You can avoid turning this into a set and simply use dissoc instead. Dissoc works on a map rather than a set, so you can avoid the extra type conversion.

user=> (dissoc cards :card1)
{:card2 {:name "Spider Tank", :type "Monster", :damage 3, :health 4, :cost 3},
 :card3 {:name "Boulder Fist Ogre", :type "Monster", :damage 6, :health 7, :cost 6}}

johnbakers February 2016

First, this is not a typical clojure idiom:

'(:card1 :card2 :card3)

This is easier and cleaner:

[:card1 :card2 :card3]

Second, you are complicating your collections a bit, in my opinion. As stated in the other comments, you cannot disj a key that is not in there; your map function is returning the values associated with the keys :card1 etc, so trying to disj the key on the results will do nothing.

Now, the fact you are turning this into a set only matters if you expect the values in your original map to possibly be duplicated in that map. Is it possible to have more than one Wisp card with the same damage, etc? If it is possible that :card5 and :card8, for example, could be identical values, then turning the map into a set will remove those duplicates. If it is not possible that cards would be identical, then the map already has unique keys that cannot be duplicated and so I'm not sure what you are gaining by transforming it into a set.

Thumbnail February 2016

deck1 is

#{{:name "Wisp", :type "Monster", :damage 1, :health 1, :cost 0, :charge "t"}
  {:name "Spider Tank", :type "Monster", :damage 3, :health 4, :cost 3}
  {:name "Boulder Fist Ogre", :type "Monster", :damage 6, :health 7, :cost 6}}

This set does not contain the value :card1. So (disj deck1 :card1) has no effect.

You want something like

(apply disj deck1 (filter #(= (:name %) "Wisp") deck1))

... which removes all elements with :name "Wisp" - there is only one, giving

#{{:name "Spider Tank", :type "Monster", :damage 3, :health 4, :cost 3}
  {:name "Boulder Fist Ogre", :type "Monster", :damage 6, :health 7, :cost 6}}

Post Status

Asked in February 2016
Viewed 3,811 times
Voted 4
Answered 4 times


Leave an answer

Quote of the day: live life