Domanda

Voglio trasformare una mappa di valori ad un altro mappa con le stesse chiavi, ma con una funzione applicata ai valori. Vorrei che ci fosse una funzione per fare questo nel api clojure, ma sono stato in grado di trovarlo.

Ecco un esempio di implementazione di quello che sto cercando

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

Qualcuno sa se map-function-on-map-vals esiste già? Vorrei pensare che ha fatto (probabilmente con un nome più bello troppo).

È stato utile?

Soluzione

Mi piace la versione reduce bene. Penso che sia idiomatica. Ecco una versione che utilizza di lista in ogni modo.

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

Altri suggerimenti

È possibile utilizzare il 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}

Ecco un modo abbastanza tipico per trasformare una mappa. zipmap prende una lista di chiavi e un elenco di valori e "fa la cosa giusta" la produzione di una nuova mappa Clojure. Si potrebbe anche mettere il map intorno ai tasti di modificarli, o entrambi.

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

o ad avvolgerlo nella funzione:

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

Preso dal Clojure Cookbook, c'è kv ridurre-:

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

Ecco un modo abbastanza idiomatico per fare questo:

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

Esempio:

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

map-map, map-map-keys, e map-map-values

Non conosco alcuna funzione esistente in Clojure per questo, ma qui è un'implementazione di tale funzione come map-map-values che si è liberi di copiare. Viene fornito con due funzioni strettamente correlate, map-map e map-map-keys, che sono anche mancano dalla libreria 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) )

Uso

È possibile chiamare map-map-values in questo modo:

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

E le altre due funzioni in questo modo:

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

implementazioni alternative

Se desideri solo map-map-keys o map-map-values, senza la funzione più generale map-map, è possibile utilizzare queste implementazioni, che non si basano su 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)} )))

Inoltre, ecco un'implementazione alternativa di map-map che si basa su clojure.walk/walk invece di < a href = "http://clojuredocs.org/clojure_core/clojure.core/into" rel = "nofollow"> into , se si preferisce questo fraseggio:

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

versioni Parellel -. pmap-map, ecc

Ci sono anche le versioni parallele di queste funzioni, se ne avete bisogno. Essi utilizzano semplicemente pmap invece di 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) )

Sono un n00b Clojure, quindi ci potrebbe essere molto più eleganti soluzioni. Ecco la mia:

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

Il func anon fa un po 'di 2-list da ogni chiave e il suo valore f'ed. Mapcat gestisce questa funzione sopra la sequenza di tasti della mappa e concatena il tutto funziona in un unico grande lista. "Applicare hash-map" crea una nuova mappa da quella sequenza. L'(% m) può sembrare un po 'strano, è idiomatica Clojure per l'applicazione di una chiave per una mappa per cercare il valore associato.

lettura più altamente raccomandato:. La Clojure Cheat Sheet

Mi piace la versione reduce. Con una leggera variazione, può anche mantenere il tipo di strutture record:

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

Il {} è stato sostituito da m. Con questo cambiamento, i record rimangono record:

(defrecord Person [firstname lastname])

(def p (map->Person {}))
(class p) '=> Person

(class (map-function-on-map-vals p
  (fn [v] (str v)))) '=> Person

Iniziando con {}, il record perde la sua recordiness , che si potrebbe desiderare mantenere, se volete le funzionalità di registrazione (rappresentazione di memoria compatta per esempio).

(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)))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top