web-dev-qa-db-ja.com

Clojureのマップの値に関数をマッピングする

同じキーを使用して値に関数を適用して、値のマップを別のマップに変換します。 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 もう存在している?私はそれをしたと思うだろう(おそらくもっといい名前でも)。

127
Thomas

私はあなたのreduceバージョンがとても気に入っています。慣用的だと思います。とにかくリスト内包表記を使用したバージョンです。

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

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

これは、マップを変換するかなり一般的な方法です。 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))))
33
Arthur Ulfeldt

Clojure Cookbookから引用したreduce-kvがあります。

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

これを行うためのかなり慣用的な方法を次に示します。

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

map-mapmap-map-keys、およびmap-map-values

このためのClojureには既存の関数はありませんが、ここではその関数をmap-map-valuesとして実装し、自由にコピーできます。 map-mapmap-map-keysの2つの密接に関連する関数が付属していますが、これらも標準ライブラリにはありません。

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

また、このフレージングを希望する場合、 into ではなく map-map に基づくclojure.walk/walkの代替実装もここにあります。

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

Parellelバージョン– 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) )
4
Rory O'Kane

私は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つの大きなリストに連結します。 「ハッシュマップの適用」は、そのシーケンスから新しいマップを作成します。 (%m)は少し奇妙に見えるかもしれませんが、キーをマップに適用して関連する値を検索するのが慣用的なClojureです。

最も強く推奨される読み物: Clojure Cheat Sheet

2
Carl Smotricz

あなたの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

{}で開始することにより、レコードはrecordinessを失います。これは、レコード機能(たとえば、コンパクトなメモリ表現)が必要な場合に保持できます。

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.