Clojureのマップの値に関数をマッピングする
-
16-09-2019 - |
質問
同じキーを持つが、値に関数が適用された1つの値のマップを別のマップに変換したいと思います。 Clojure APIでこれを行う機能があると思いますが、見つけることができませんでした。
これが私が探しているものの実装の例です
(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}
誰かが知っているかどうかを知っていますか map-function-on-map-vals
もう存在している?私はそれがしたと思うでしょう(おそらく名前もあります)。
解決
私はあなたが好きです reduce
バージョンは大丈夫です。慣用的だと思います。とにかくリスト理解を使用するバージョンは次のとおりです。
(defn foo [m f]
(into {} (for [[k v] m] [k (f v)])))
他のヒント
使用できます 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}
ここに、マップを変換するかなり典型的な方法があります。 zipmap
キーのリストと値のリストを撮影し、新しいClojureマップを作成する「正しいことを行う」。あなたも置くことができます map
それらを変更するための鍵の周り、またはその両方。
(zipmap (keys data) (map #(do-stuff %) (vals data)))
またはあなたの関数にそれを包むために:
(defn map-function-on-map-vals [m f]
(zipmap (keys m) (map f (vals m))))
Clojure Cookbookから取られて、decose-kvがあります:
(defn map-kv [m f]
(reduce-kv #(assoc %1 %2 (f %3)) {} m))
これを行うためのかなり慣用的な方法は次のとおりです。
(defn map-function-on-map-vals [m f]
(apply merge
(map (fn [[k v]] {k (f v)})
m)))
例:
user> (map-function-on-map-vals {1 1, 2 2, 3 3} inc))
{3 4, 2 3, 1 2}
map-map
, map-map-keys
, 、 と map-map-values
これについては、Clojureに既存の機能がないことは知っていますが、次のようにその機能の実装があります map-map-values
あなたが自由にコピーできること。密接に関連する2つの機能が付属しています。 map-map
と map-map-keys
, 、標準ライブラリにも欠落しています。
(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) )
使用法
あなたは電話することができます map-map-values
このような:
(map-map-values str {:a 1 :b 2})
;; => {:a "1", :b "2"}
そして、このような他の2つの機能:
(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}
代替実装
あなたが欲しいなら map-map-keys
また map-map-values
, 、より一般的ではありません map-map
関数、これらの実装を使用できます。 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)} )))
また、ここに代替の実装があります map-map
それはに基づいています clojure.walk/walk
それ以外の into
, 、あなたがこの言い回しを好むなら:
(defn map-map [f m]
(clojure.walk/walk #(apply f %) identity m) )
パレルバージョン - pmap-map
, 、など
必要な場合、これらの関数には並列バージョンもあります。彼らは単に使用します pmap
それ以外の 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) )
私はClojure N00Bなので、はるかにエレガントなソリューションがあるかもしれません。これが私のものです:
(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))
Anon Funcは、各キーとそのF'ed値から少し2リストを作成します。 MAPCATは、この関数をマップのキーのシーケンスで実行し、作業全体を1つの大きなリストに連結します。 「Hash-Mapを適用」すると、そのシーケンスから新しいマップが作成されます。 (%m)は少し奇妙に見えるかもしれません。関連する値を調べるためにマップにキーを適用するのは慣用的なクロジュアです。
最も強く推奨される読書: Clojureチートシート .
私はあなたが好きです reduce
バージョン。非常にわずかなバリエーションで、レコード構造のタイプを保持することもできます。
(defn map-function-on-map-vals [m f]
(reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) m m))
{}
置き換えられました m
. 。その変更により、レコードは記録のままです。
(defrecord Person [firstname lastname])
(def p (map->Person {}))
(class p) '=> Person
(class (map-function-on-map-vals p
(fn [v] (str v)))) '=> Person
最初から {}
, 、レコードはそれを失います 記録性, 、レコード機能(たとえばコンパクトなメモリ表現)を希望する場合は、保持したいと思うかもしれません。
(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)))