Je veux transformer une carte de valeurs en une autre carte avec les mêmes clés mais avec une fonction appliquée aux valeurs. Je penserais qu'il y avait une fonction pour faire cela dans l'api clojure, mais je n'ai pas pu le trouver.
Voici un exemple d'implémentation de ce que je recherche
(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}
Est-ce que quelqu'un sait si map-function-on-map-vals
existe déjà? Je penserais que c'était le cas (probablement avec un nom plus joli aussi).
J'aime bien votre version reduce
. Je pense que c'est idiomatique. Voici une version utilisant la compréhension de liste de toute façon.
(defn foo [m f]
(into {} (for [[k v] m] [k (f v)])))
Vous pouvez utiliser le 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}
Voici un moyen assez classique de transformer une carte zipmap
prend une liste de clés et une liste de valeurs et "fait ce qu'il faut" pour produire une nouvelle carte Clojure. Vous pouvez également mettre la map
autour des touches pour les changer, ou les deux.
(zipmap (keys data) (map #(do-stuff %) (vals data)))
ou pour envelopper dans votre fonction:
(defn map-function-on-map-vals [m f]
(zipmap (keys m) (map f (vals m))))
Tiré du livre de cuisine de Clojure, il existe une réduction de kv:
(defn map-kv [m f]
(reduce-kv #(assoc %1 %2 (f %3)) {} m))
Voici une façon assez idiomatique de procéder:
(defn map-function-on-map-vals [m f]
(apply merge
(map (fn [[k v]] {k (f v)})
m)))
Exemple:
user> (map-function-on-map-vals {1 1, 2 2, 3 3} inc))
{3 4, 2 3, 1 2}
map-map
, map-map-keys
et map-map-values
Je ne connais aucune fonction existante dans Clojure pour cela, mais voici une implémentation de cette fonction en tant que map-map-values
que vous êtes libre de copier. Il contient deux fonctions étroitement liées, map-map
et map-map-keys
, qui sont également absentes de la bibliothèque standard:
(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) )
Vous pouvez appeler map-map-values
comme ceci:
(map-map-values str {:a 1 :b 2})
;; => {:a "1", :b "2"}
Et les deux autres fonctions sont les suivantes:
(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}
Si vous ne voulez que map-map-keys
ou map-map-values
, sans la fonction plus générale map-map
, vous pouvez utiliser ces implémentations, qui ne reposent pas sur 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)} )))
En outre, voici une autre implémentation de map-map
qui est basée sur clojure.walk/walk
au lieu de into
, si vous préférez cette formulation:
(defn map-map [f m]
(clojure.walk/walk #(apply f %) identity m) )
pmap-map
, etc.Il existe également des versions parallèles de ces fonctions si vous en avez besoin. Ils utilisent simplement pmap
au lieu 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) )
Je suis un Clojure n00b, il pourrait donc y avoir des solutions beaucoup plus élégantes. Voilà le mien:
(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))
La fonction anon crée une petite liste de 2 à partir de chaque touche et de sa valeur fédée. Mapcat exécute cette fonction sur la séquence des touches de la carte et concatène l'ensemble des œuvres dans une grande liste. "apply hash-map" crée une nouvelle carte à partir de cette séquence. Le (% m) peut sembler un peu étrange, il est idiomatique de appliquer une clé à une carte pour rechercher la valeur associée.
Lecture fortement recommandée: The Clojure Cheat Sheet .
J'aime votre version reduce
. Avec une très légère variation, il peut également conserver le type de structure des enregistrements:
(defn map-function-on-map-vals [m f]
(reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) m m))
Le {}
a été remplacé par m
. Avec ce changement, les enregistrements restent des enregistrements:
(defrecord Person [firstname lastname])
(def p (map->Person {}))
(class p) '=> Person
(class (map-function-on-map-vals p
(fn [v] (str v)))) '=> Person
En commençant par {}
, l’enregistrement perd son recordiness , qu’on peut vouloir conserver, si vous souhaitez utiliser les capacités d’enregistrement (représentation en mémoire compacte, par exemple).
(defn map-vals
"Map f over every value of m.
Returns a map with the same keys as m, where each of its values is now the result of applying f to them one by one.
f is a function of one arg, which will be called which each value of m, and should return the new value.
Faster then map-vals-transient on small maps (8 elements and under)"
[f m]
(reduce-kv (fn [m k v]
(assoc m k (f v)))
{} m))
(defn map-vals-transient
"Map f over every value of m.
Returns a map with the same keys as m, where each of its values is now the result of applying f to them one by one.
f is a function of one arg, which will be called which each value of m, and should return the new value.
Faster then map-vals on big maps (9 elements or more)"
[f m]
(persistent! (reduce-kv (fn [m k v]
(assoc! m k (f v)))
(transient {}) m)))