conj dictionary clojure map-function

dictionary - conj - Mapeo de una función en los valores de un mapa en Clojure



reduce clojure (8)

Quiero transformar un mapa de valores en otro mapa con las mismas teclas pero con una función aplicada a los valores. Pensaría que había una función para hacer esto en la API de Clojure, pero no he podido encontrarla.

Aquí hay una implementación de ejemplo de lo que estoy buscando

(defn map-function-on-map-vals [m f] (reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) {} m)) (println (map-function-on-map-vals {:a "test" :b "testing"} #(.toUpperCase %))) {:b TESTING, :a TEST}

¿Alguien sabe si map-function-on-map-vals ya existe? Creo que sí (probablemente con un nombre más agradable también).


map-map , map-map-keys , y map-map-values

No conozco ninguna función existente en Clojure para esto, pero aquí hay una implementación de esa función como map-map-values que puede copiar. Viene con dos funciones estrechamente relacionadas, map-map y map-map-keys , que también faltan en la biblioteca estándar:

(defn map-map "Returns a new map with each key-value pair in `m` transformed by `f`. `f` takes the arguments `[key value]` and should return a value castable to a map entry, such as `{transformed-key transformed-value}`." [f m] (into (empty m) (map #(apply f %) m)) ) (defn map-map-keys [f m] (map-map (fn [key value] {(f key) value}) m) ) (defn map-map-values [f m] (map-map (fn [key value] {key (f value)}) m) )

Uso

Puede llamar a map-map-values esta manera:

(map-map-values str {:a 1 :b 2}) ;; => {:a "1", :b "2"}

Y las otras dos funciones como esta:

(map-map-keys str {:a 1 :b 2}) ;; => {":a" 1, ":b" 2} (map-map (fn [k v] {v k}) {:a 1 :b 2}) ;; => {1 :a, 2 :b}

Implementaciones alternativas

Si solo quiere map-map-keys o map-map-values , sin la función más general de map-map , puede usar estas implementaciones, que no se basan en map-map :

(defn map-map-keys [f m] (into (empty m) (for [[key value] m] {(f key) value} ))) (defn map-map-values [f m] (into (empty m) (for [[key value] m] {key (f value)} )))

Además, aquí hay una implementación alternativa de map-map que se basa en clojure.walk/walk lugar de into , si prefieres este fraseo:

(defn map-map [f m] (clojure.walk/walk #(apply f %) identity m) )

Versiones de Parellel - pmap-map , etc.

También hay versiones paralelas de estas funciones si las necesita. Simplemente usan pmap lugar de map .

(defn pmap-map [f m] (into (empty m) (pmap #(apply f %) m)) ) (defn pmap-map-keys [f m] (pmap-map (fn [key value] {(f key) value}) m) ) (defn pmap-map-values [f m] (pmap-map (fn [key value] {key (f value)}) m) )


Aquí hay una forma bastante idiomática de hacer esto:

(defn map-function-on-map-vals [m f] (apply merge (map (fn [[k v]] {k (f v)}) m)))

Ejemplo:

user> (map-function-on-map-vals {1 1, 2 2, 3 3} inc)) {3 4, 2 3, 1 2}


Aquí hay una forma bastante típica de transformar un mapa. zipmap toma una lista de claves y una lista de valores y "hace lo correcto" produciendo un nuevo mapa de Clojure. También podría colocar el map alrededor de las teclas para cambiarlas, o ambas cosas.

(zipmap (keys data) (map #(do-stuff %) (vals data)))

o para envolverlo en tu función:

(defn map-function-on-map-vals [m f] (zipmap (keys m) (map f (vals m))))


Me gusta tu versión reducida muy bien. Creo que es idiomático Aquí hay una versión que usa la lista de comprensión de todos modos.

(defn foo [m f] (into {} (for [[k v] m] [k (f v)])))


Me gusta tu versión reducida. Con una ligera variación, también puede conservar el tipo de estructuras de registros:

(defn map-function-on-map-vals [m f] (reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) m m))

{} Fue reemplazado por m . Con ese cambio, los registros siguen siendo registros:

(defrecord Person [firstname lastname]) (def p (map->Person {})) (class p) ''=> Person (class (map-function-on-map-vals p (fn [v] (str v)))) ''=> Person

Al comenzar con {} , el registro pierde su capacidad de grabar , que es posible que desee retener, si desea las capacidades de grabación (por ejemplo, representación de memoria compacta).


Puede usar clojure.algo.generic.functor/fmap :

user=> (use ''[clojure.algo.generic.functor :only (fmap)]) nil user=> (fmap inc {:a 1 :b 3 :c 5}) {:a 2, :b 4, :c 6}


Soy Clojure n00b, por lo que puede haber soluciones mucho más elegantes. Aquí está el mío:

(def example {:a 1 :b 2 :c 3 :d 4}) (def func #(* % %)) (prn example) (defn remap [m f] (apply hash-map (mapcat #(list % (f (% m))) (keys m)))) (prn (remap example func))

El anon func hace una pequeña lista de 2 de cada tecla y su valor f''ed. Mapcat ejecuta esta función sobre la secuencia de las claves del mapa y concatena todo el trabajo en una sola lista grande. "aplicar hash-map" crea un nuevo mapa a partir de esa secuencia. El (% m) puede parecer un poco raro, es Clojure idiomático para aplicar una clave a un mapa para buscar el valor asociado.

Lectura más recomendada: La Hoja de trucos de Clojure .


Tomado del libro de cocina Clojure, hay reduce-kv:

(defn map-kv [m f] (reduce-kv #(assoc %1 %2 (f %3)) {} m))