Zuordnung einer Funktion auf den Werten einer Karte in Clojure
-
16-09-2019 - |
Frage
Ich möchte eine Karte der Werte mit denselben Tasten in eine andere Karte umwandeln, jedoch mit einer auf die Werte angewendeten Funktion. Ich würde denken, es gab eine Funktion dafür in der Clojure -API, aber ich konnte sie nicht finden.
Hier ist eine Beispielimplementierung dessen, wonach 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
ist bereits vorhanden? Ich würde denken, es tat (wahrscheinlich auch mit einem schöneren Namen).
Lösung
Ich mag deine reduce
Version gut. Ich denke, es ist idiomatisch. Hier ist eine Version, die das Listenverständnis sowieso verwendet.
(defn foo [m f]
(into {} (for [[k v] m] [k (f v)])))
Andere Tipps
Du kannst den ... benutzen 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}
Hier ist eine ziemlich typische Möglichkeit, eine Karte zu transformieren. zipmap
Nimmt eine Liste von Schlüsseln und eine Liste von Werten und "das Richtige", die eine neue Clojure -Karte erzeugt. Sie könnten das auch setzen map
um die Schlüssel, um sie oder beides zu ändern.
(zipmap (keys data) (map #(do-stuff %) (vals data)))
oder um es in Ihre Funktion einzuwickeln:
(defn map-function-on-map-vals [m f]
(zipmap (keys m) (map f (vals m))))
Aus dem Clojure-Kochbuch entnommen, gibt es record-kV:
(defn map-kv [m f]
(reduce-kv #(assoc %1 %2 (f %3)) {} m))
Hier ist eine ziemlich idiomatische Art, 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}
map-map
, map-map-keys
, und map-map-values
Ich kenne dafür keine vorhandene Funktion in Clojure, aber hier ist eine Implementierung dieser Funktion als map-map-values
dass Sie frei zu kopieren sind. Es kommt mit zwei eng verwandten Funktionen, map-map
und map-map-keys
, die auch 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
Du kannst anrufen map-map-values
so was:
(map-map-values str {:a 1 :b 2})
;; => {:a "1", :b "2"}
Und die anderen beiden Funktionen wie diese:
(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 wollen map-map-keys
oder map-map-values
, ohne die allgemeineren map-map
Funktion können Sie diese Implementierungen verwenden, auf die sich nicht verlassen 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)} )))
Hier ist auch eine alternative Implementierung von map-map
das basiert auf clojure.walk/walk
Anstatt von into
, Wenn Sie diese Phrasierung bevorzugen:
(defn map-map [f m]
(clojure.walk/walk #(apply f %) identity m) )
Parellenversionen - pmap-map
, etc.
Es gibt auch parallele Versionen dieser Funktionen, wenn Sie sie benötigen. Sie benutzen einfach pmap
Anstatt 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) )
Ich bin ein Clojure N00B, daher gibt es möglicherweise 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))
Der Anon-Func macht eine kleine 2-Liste von jedem Schlüssel und seinem Wert. MAPCAT führt diese Funktion über die Abfolge der Tasten der Karte aus und verkettet die gesamte Arbeit zu einer großen Liste. "anwenden hash-map" erstellt eine neue Karte aus dieser Sequenz. Das (% m) sieht vielleicht etwas seltsam aus, es ist idiomatische Klosel, um einen Schlüssel auf eine Karte anzuwenden, um den zugehörigen Wert zu suchen.
Am dringend empfohlenes Lesen: die Clojure -Cheat -Blatt .
Ich mag deine reduce
Ausführung. Mit einer sehr geringen Variation kann es auch die Art der Datensätze behalten:
(defn map-function-on-map-vals [m f]
(reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) m m))
Das {}
wurde durch m
. Mit dieser Änderung bleiben Aufzeichnungen auf Aufzeichnungen:
(defrecord Person [firstname lastname])
(def p (map->Person {}))
(class p) '=> Person
(class (map-function-on-map-vals p
(fn [v] (str v)))) '=> Person
Durch anfangen mit {}
, die Aufzeichnung verliert seine Aufzeichnung, welches vielleicht beibehalten möchte, wenn Sie die Datensatzfunktionen wünschen (beispielsweise kompakte Speicherdarstellung).
(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)))