6
votes

Still working through Programming Collective Intelligence and using Clojure to write the code. I've got it working, but some parts are really ugly, so I thought I'd ask some of the experts around here to help clean it up.

Let's suppose I have a map that looks like this (bound to "recs"):

{"Superman Returns" 3.902419556891574, "Lady in the Water" 2.8325499182641614, 
"Snakes on a Plane" 3.7059737842895792, "The Night Listener" 3.3477895267131017,
"You, Me and Dupree" 2.651006036204627, "Just My Luck" 2.5309807037655645}

and I want to remove those items with keys that are also in the map (bound to "mymovies"):

{"Snakes on a Plane" 4.5, "You, Me and Dupree" 1.0, "Superman Returns" 4.0}

so that I get the map:

{"Lady in the Water" 2.8325499182641614, "The Night Listener" 3.3477895267131017,
"Just My Luck" 2.5309807037655645}

the code that I managed to get to do this looks like:

(apply merge (map #(hash-map (first %) (second %))
              (remove #(contains? mymovies (first %))
                      recs)))

That seems pretty ugly to me. It doesn't seem like it should be necessary to create a map from the value I get back from "remove". Is there a cleaner way to do this?

UPDATE: Joost's answer below sparked another idea. If I turn the keys of the two maps into sets I can use select-keys like this:

(select-keys recs (difference (set (keys recs))
                              (set (keys mymovies))))

Joost, thanks for turning me on to select-keys. I didn't know about that function before. Now to go rewrite several other sections with this new found knowledge!

3

3 Answers

13
votes
(apply dissoc recs (keys mymovies))
2
votes

The following first builds a seq of keys to keep, then extracts the "submap" for those keys from recs using select-keys. It also takes advantage of the fact that sets are predicates.

(select-keys recs (remove (apply hash-set (keys mymovies)) (keys recs)))
0
votes

I think ponzao's answer is best for this case, but I wouldn't have thought to apply dissoc. Here are the two solutions I might have come up with: hopefully looking over them will help with similar future problems.

Note that the second solution will fail if your mymovies map contains nil or false values.

(into {}
      (for [[k v] recs
            :when (not (contains? mymovies k))]
        [k v]))

(into {}
      (remove (comp mymovies key) recs))