Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 532
rzv Avatar asked Jan 30 '13 05:01

rzv


3 Answers

(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.

like image 148
kotarak Avatar answered Sep 24 '22 10:09

kotarak


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.

like image 37
Matt Fenwick Avatar answered Sep 20 '22 10:09

Matt Fenwick


(letfn [(map-vals [m f]
          (into {} (for [[k v] m]
                     [k (f v)])))]
  (map-vals m #(= Integer (type %))))
like image 41
amalloy Avatar answered Sep 21 '22 10:09

amalloy