我想将一个值映射转换为另一个具有相同键但具有应用于值的函数的映射。我认为在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
是否已经存在?我认为它确实(可能还有一个更好的名字)。
答案 0 :(得分:140)
我很喜欢你的reduce
版本。我认为这是惯用的。这是一个使用列表理解的版本。
(defn foo [m f]
(into {} (for [[k v] m] [k (f v)])))
答案 1 :(得分:91)
您可以使用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}
答案 2 :(得分:33)
这是一种转换地图的典型方法。
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))))
答案 3 :(得分:17)
取自Clojure Cookbook,有还原-kv:
(defn map-kv [m f]
(reduce-kv #(assoc %1 %2 (f %3)) {} m))
答案 4 :(得分:8)
这是一种相当惯用的方法:
(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}
答案 5 :(得分:4)
map-map
,map-map-keys
和map-map-values
我知道Clojure中没有现有函数,但是这里有一个函数map-map-values
可以自由复制。它附带了两个密切相关的函数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"}
另外两个函数是这样的:
(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) )
答案 6 :(得分:2)
我是一个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在地图键的序列上运行此功能,并将整个作品连接成一个大的列表。 “apply hash-map”从该序列创建一个新映射。 (%m)可能看起来有点奇怪,它是用于将键应用于地图以查找相关值的惯用Clojure。
最强烈推荐的阅读:Clojure Cheat Sheet。
答案 7 :(得分:1)
我喜欢你的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
从{}
开始,如果您需要记录功能(例如紧凑的内存表示),则记录会丢失其记录,这可能是想要保留的。
答案 8 :(得分:0)
Route::post
答案 9 :(得分:0)
我想知道为什么还没有人提到specter库。编写该文件是为了使这种转换易于编码(更重要的是,编写的代码易于理解),同时仍然非常高效:
(require '[com.rpl.specter :as specter])
(defn map-vals [m f]
(specter/transform
[specter/ALL specter/LAST]
f m))
(map-vals {:a "test" :b "testing"}
#(.toUpperCase %))
在纯Clojure中编写这样的函数很简单,但是一旦转到由不同数据结构组成的高度嵌套的代码,代码就会变得更加棘手。这就是幽灵出现的地方。
我建议您在Clojure电视上观看this episode,该视频解释了幽灵背后的动机和细节。
答案 10 :(得分:0)