rzv
rzv

Reputation: 2022

Map over each value in hash-map

I have a hash-map. I want to iterate over the values and replace each of them depending on the value's type. If the value is an integer, replace it with true, and if not, replace it with false. I want this to return a new hash-map with every value updated to either true or false.

(defn my-function [hash-map]
  (loop [hash-map hash-map]
    (for [value (vals hash-map)]
      (if (= Integer (type value))
        (recur (assoc hash-map key true))
        (recur (assoc hash-map key false))))))

That won't work because Clojure Can only recur from tail position, but that's the general idea of what I want to do. Any ideas of an effective way to do this? if-let and update-in seemed like potential solutions, but I can't quite grasp them.

Upvotes: 11

Views: 7148

Answers (4)

Matt Fenwick
Matt Fenwick

Reputation: 49085

The operation you've outlined -- transforming each value in a map, independently -- is actually already implemented in the Functor module.

What you need to do to use it is implement your function that transforms a single value, then fmap it over your map:

(fmap your-function your-map)

(Don't be misled by the name fmap -- this operation is not specific to maps. Since it's a generic function, it works on anything that has a Functor instance, which also includes lists, sets, and vectors). This is a structure-preserving operation: none of the keys will be changed, no new ones will be added, none will be removed.

If you'd prefer to avoid using a generic function, just check out the implementation:

(defmethod fmap clojure.lang.IPersistentMap
  [f m]
  (into (empty m) (for [[k v] m] [k (f v)])))  ;;; <== the important part!!

where f = your-function and m = your-map.


This library has been moved (is moving? will be moved?) to clojure.algo.generic.functor. See this for more information, and this for the source.

Upvotes: 5

kotarak
kotarak

Reputation: 17299

(reduce-kv (fn [m k v] (assoc m k (= Integer (type v)))) {} m)

Or even shorter if you prefer:

(reduce-kv #(assoc %1 %2 (= Integer (type %3))) {} m)

And to keep the type of map (hash vs. sorted):

(reduce-kv #(assoc %1 %2 (= Integer (type %3))) (empty m) m)

Caveat: the last one doesn't work with records.

Upvotes: 18

mobyte
mobyte

Reputation: 3752

(defn f [m]
  (reduce (fn [res [k v]] 
            (assoc res k (= Integer (type v)))) 
          {} m))

Or if you need recursion version

(defn f 
  ([m] (f {} m))
  ([res m] (if (empty? m)
             res
             (let [[k v] (first m)]
               (recur (assoc res k (= Integer (type v))) 
                      (rest m))))))

Upvotes: 0

amalloy
amalloy

Reputation: 91857

(letfn [(map-vals [m f]
          (into {} (for [[k v] m]
                     [k (f v)])))]
  (map-vals m #(= Integer (type %))))

Upvotes: 3

Related Questions