webentwicklung-frage-antwort-db.com.de

Zuordnung einer Funktion zu den Werten einer Karte in Clojure

Ich möchte eine Map von Werten in eine andere Map mit den gleichen Tasten, aber mit einer auf die Werte angewendeten Funktion transformieren. Ich würde denken, dass es dafür eine Funktion in der Clojure-API gibt, aber ich konnte sie nicht finden. 

Hier ist eine Beispielimplementierung dessen, was ich suche

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

Weiß jemand, ob map-function-on-map-vals bereits existiert? Ich würde meinen, wahrscheinlich auch mit einem schöneren Namen.

122
Thomas

Ihre reduce-Version gefällt mir gut. Ich finde es idiomatisch. Hier ist eine Version, die sowieso Listenverständnis verwendet.

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

Sie können den clojure.algo.generic.functor/fmap verwenden:

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

Hier ist eine ziemlich typische Methode zum Transformieren einer Karte zipmap nimmt eine Liste von Schlüsseln und eine Liste von Werten und "tut das Richtige", um eine neue Clojure-Karte zu erstellen. Sie können die map auch um die Tasten legen, um sie oder beide zu ändern. 

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

oder um es in Ihrer Funktion zusammenzufassen:

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

Aus dem Clojure-Kochbuch entnommen, gibt es den redu-kv:

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

Hier ist ein ziemlich idiomatischer Weg, dies zu tun:

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

Beispiel:

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

map-map, map-map-keys und map-map-values

Ich kenne dafür keine Funktion in Clojure, aber hier ist eine Implementierung dieser Funktion als map-map-values, die Sie kopieren können. Es verfügt über zwei eng miteinander verbundene Funktionen, map-map und map-map-keys, die ebenfalls in der Standardbibliothek fehlen:

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

Verwendungszweck

Sie können map-map-values folgendermaßen aufrufen:

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

Und die anderen beiden Funktionen wie folgt:

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

Alternative Implementierungen

Wenn Sie nur map-map-keys oder map-map-values ohne die allgemeinere map-map-Funktion möchten, können Sie diese Implementierungen verwenden, die nicht auf map-map angewiesen sind:

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

Hier ist auch eine alternative Implementierung von map-map, die auf clojure.walk/walk anstelle von into basiert, wenn Sie diese Formulierung bevorzugen:

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

Parallelversionen - pmap-map usw.

Es gibt auch parallele Versionen dieser Funktionen, falls Sie diese benötigen. Sie verwenden einfach pmap anstelle von 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) )
4
Rory O'Kane

Ich bin ein Clojure n00b, daher gibt es vielleicht viel elegantere Lösungen. Hier ist meins:

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

Die anon-Funktion macht aus jeder Taste und ihrem f'ed-Wert eine kleine 2-Liste. Mapcat führt diese Funktion über die Reihenfolge der Schlüssel der Karte aus und verkettet die gesamten Arbeiten in einer großen Liste. "hash-map anwenden" erstellt eine neue Karte aus dieser Sequenz. Das (% m) kann ein bisschen komisch aussehen, es ist ein idiomatisches Clojure, das einen Schlüssel auf eine Karte anwendet, um den zugehörigen Wert nachzuschlagen.

Am meisten empfohlene Lektüre: Das Clojure Cheat Sheet .

2
Carl Smotricz

Ich mag deine reduce Version. Mit einer sehr geringen Abweichung kann es auch die Art der Datensatzstrukturen beibehalten:

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

Der {} wurde durch m ersetzt. Mit dieser Änderung bleiben Datensätze Datensätze:

(defrecord Person [firstname lastname])

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

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

Wenn Sie mit {} beginnen, verliert der Datensatz seine recordiness , die möglicherweise beibehalten werden soll, wenn Sie die Aufzeichnungsfunktionen wünschen (zum Beispiel eine kompakte Speicherdarstellung).

1
olange
(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)))
0
Didier A.