使用 clojure,有没有更好的方法从序列中删除项目,这是地图中的值?
Using clojure, Is there a better way to to remove a item from a sequence, which is the value in a map?
有一张包含序列的地图。序列包含项目。
我想从包含它的任何序列中删除给定的项目。
我找到的解决方案做了它应该做的,但我想知道是否有更好的
或更优雅的方式来实现同样的目标。
我目前的解决方案:
(defn remove-item-from-map-value [my-map item]
(apply merge (for [[k v] my-map] {k (remove #(= item %) v)})))
测试描述了预期的行为:
(require '[clojure.test :as t])
(def my-map {:keyOne ["itemOne"]
:keyTwo ["itemTwo" "itemThree"]
:keyThree ["itemFour" "itemFive" "itemSix"]})
(defn remove-item-from-map-value [my-map item]
(apply merge (for [[k v] my-map] {k (remove #(= item %) v)})))
(t/is (= (remove-item-from-map-value my-map "unknown-item") my-map))
(t/is (= (remove-item-from-map-value my-map "itemFive") {:keyOne ["itemOne"]
:keyTwo ["itemTwo" "itemThree"]
:keyThree ["itemFour" "itemSix"]}))
(t/is (= (remove-item-from-map-value my-map "itemThree") {:keyOne ["itemOne"]
:keyTwo ["itemTwo"]
:keyThree ["itemFour" "itemFive" "itemSix"]}))
(t/is (= (remove-item-from-map-value my-map "itemOne") {:keyOne []
:keyTwo ["itemTwo" "itemThree"]
:keyThree ["itemFour" "itemFive" "itemSix"]}))
我是 clojure 的新手,对不同的解决方案很感兴趣。
所以欢迎任何意见。
我认为您的解决方案基本没问题,但我会尽量避免使用 apply merge
部分,因为您可以使用 into
轻松地从序列重新创建地图。此外,您还可以使用 map
而不是 for
,我认为在这种情况下更符合习惯,因为您不使用 for
的任何列表理解功能。
(defn remove-item-from-map-value [m item]
(->> m
(map (fn [[k vs]]
{k (remove #(= item %) vs)}))
(into {})))
我会选择这样的东西:
user> (defn remove-item [my-map item]
(into {}
(map (fn [[k v]] [k (remove #{item} v)]))
my-map))
#'user/remove-item
user> (remove-item my-map "itemFour")
;;=> {:keyOne ("itemOne"),
;; :keyTwo ("itemTwo" "itemThree"),
;; :keyThree ("itemFive" "itemSix")}
您还可以编写一个方便的函数 map-val
对映射值执行映射:
(defn map-val [f data]
(reduce-kv
(fn [acc k v] (assoc acc k (f v)))
{} data))
或者像这样简短地说:
(defn map-val [f data]
(reduce #(update % %2 f) data (keys data)))
user> (map-val inc {:a 1 :b 2})
;;=> {:a 2, :b 3}
(defn remove-item [my-map item]
(map-val (partial remove #{item}) my-map))
user> (remove-item my-map "itemFour")
;;=> {:keyOne ("itemOne"),
;; :keyTwo ("itemTwo" "itemThree"),
;; :keyThree ("itemFive" "itemSix")}
我投入 specter
版本的好措施。它将矢量保存在地图内
而且真的很紧凑。
(setval [MAP-VALS ALL #{"itemFive"}] NONE my-map)
示例
user=> (use 'com.rpl.specter)
nil
user=> (def my-map {:keyOne ["itemOne"]
#_=> :keyTwo ["itemTwo" "itemThree"]
#_=> :keyThree ["itemFour" "itemFive" "itemSix"]})
#_=>
#'user/my-map
user=> (setval [MAP-VALS ALL #{"itemFive"}] NONE my-map)
{:keyOne ["itemOne"],
:keyThree ["itemFour" "itemSix"],
:keyTwo ["itemTwo" "itemThree"]}
user=> (setval [MAP-VALS ALL #{"unknown"}] NONE my-map)
{:keyOne ["itemOne"],
:keyThree ["itemFour" "itemFive" "itemSix"],
:keyTwo ["itemTwo" "itemThree"]}
另一个很像@leetwinski 的解决方案:
(defn remove-item [m i]
(zipmap (keys m)
(map (fn [v] (remove #(= % i) v))
(vals m))))
这是一个以优雅的方式完成此操作的单行代码。我在这种情况下使用的完美功能是 clojure.walk/prewalk
。这个 fn 所做的是遍历您传递给它的表单的所有子表单,并使用提供的 fn:
转换它们
(defn remove-item-from-map-value [data item]
(clojure.walk/prewalk #(if (map-entry? %) [(first %) (remove #{item} (second %))] %) data))
remove-item-from-map-value
fn 会做的是检查当前表单是否是映射条目,如果是,它将从其值中删除指定的键(映射条目的第二个元素,它是一个向量分别包含一个键和一个值)。
这种方法最好的一点是它是完全可扩展的:您可以决定对不同类型的表单做不同的事情,您还可以处理嵌套表单等。
我花了一些时间来掌握这个 fn,但当我掌握它后,我发现它非常有用!
有一张包含序列的地图。序列包含项目。 我想从包含它的任何序列中删除给定的项目。
我找到的解决方案做了它应该做的,但我想知道是否有更好的 或更优雅的方式来实现同样的目标。
我目前的解决方案:
(defn remove-item-from-map-value [my-map item]
(apply merge (for [[k v] my-map] {k (remove #(= item %) v)})))
测试描述了预期的行为:
(require '[clojure.test :as t])
(def my-map {:keyOne ["itemOne"]
:keyTwo ["itemTwo" "itemThree"]
:keyThree ["itemFour" "itemFive" "itemSix"]})
(defn remove-item-from-map-value [my-map item]
(apply merge (for [[k v] my-map] {k (remove #(= item %) v)})))
(t/is (= (remove-item-from-map-value my-map "unknown-item") my-map))
(t/is (= (remove-item-from-map-value my-map "itemFive") {:keyOne ["itemOne"]
:keyTwo ["itemTwo" "itemThree"]
:keyThree ["itemFour" "itemSix"]}))
(t/is (= (remove-item-from-map-value my-map "itemThree") {:keyOne ["itemOne"]
:keyTwo ["itemTwo"]
:keyThree ["itemFour" "itemFive" "itemSix"]}))
(t/is (= (remove-item-from-map-value my-map "itemOne") {:keyOne []
:keyTwo ["itemTwo" "itemThree"]
:keyThree ["itemFour" "itemFive" "itemSix"]}))
我是 clojure 的新手,对不同的解决方案很感兴趣。 所以欢迎任何意见。
我认为您的解决方案基本没问题,但我会尽量避免使用 apply merge
部分,因为您可以使用 into
轻松地从序列重新创建地图。此外,您还可以使用 map
而不是 for
,我认为在这种情况下更符合习惯,因为您不使用 for
的任何列表理解功能。
(defn remove-item-from-map-value [m item]
(->> m
(map (fn [[k vs]]
{k (remove #(= item %) vs)}))
(into {})))
我会选择这样的东西:
user> (defn remove-item [my-map item]
(into {}
(map (fn [[k v]] [k (remove #{item} v)]))
my-map))
#'user/remove-item
user> (remove-item my-map "itemFour")
;;=> {:keyOne ("itemOne"),
;; :keyTwo ("itemTwo" "itemThree"),
;; :keyThree ("itemFive" "itemSix")}
您还可以编写一个方便的函数 map-val
对映射值执行映射:
(defn map-val [f data]
(reduce-kv
(fn [acc k v] (assoc acc k (f v)))
{} data))
或者像这样简短地说:
(defn map-val [f data]
(reduce #(update % %2 f) data (keys data)))
user> (map-val inc {:a 1 :b 2})
;;=> {:a 2, :b 3}
(defn remove-item [my-map item]
(map-val (partial remove #{item}) my-map))
user> (remove-item my-map "itemFour")
;;=> {:keyOne ("itemOne"),
;; :keyTwo ("itemTwo" "itemThree"),
;; :keyThree ("itemFive" "itemSix")}
我投入 specter 版本的好措施。它将矢量保存在地图内 而且真的很紧凑。
(setval [MAP-VALS ALL #{"itemFive"}] NONE my-map)
示例
user=> (use 'com.rpl.specter)
nil
user=> (def my-map {:keyOne ["itemOne"]
#_=> :keyTwo ["itemTwo" "itemThree"]
#_=> :keyThree ["itemFour" "itemFive" "itemSix"]})
#_=>
#'user/my-map
user=> (setval [MAP-VALS ALL #{"itemFive"}] NONE my-map)
{:keyOne ["itemOne"],
:keyThree ["itemFour" "itemSix"],
:keyTwo ["itemTwo" "itemThree"]}
user=> (setval [MAP-VALS ALL #{"unknown"}] NONE my-map)
{:keyOne ["itemOne"],
:keyThree ["itemFour" "itemFive" "itemSix"],
:keyTwo ["itemTwo" "itemThree"]}
另一个很像@leetwinski 的解决方案:
(defn remove-item [m i]
(zipmap (keys m)
(map (fn [v] (remove #(= % i) v))
(vals m))))
这是一个以优雅的方式完成此操作的单行代码。我在这种情况下使用的完美功能是 clojure.walk/prewalk
。这个 fn 所做的是遍历您传递给它的表单的所有子表单,并使用提供的 fn:
(defn remove-item-from-map-value [data item]
(clojure.walk/prewalk #(if (map-entry? %) [(first %) (remove #{item} (second %))] %) data))
remove-item-from-map-value
fn 会做的是检查当前表单是否是映射条目,如果是,它将从其值中删除指定的键(映射条目的第二个元素,它是一个向量分别包含一个键和一个值)。
这种方法最好的一点是它是完全可扩展的:您可以决定对不同类型的表单做不同的事情,您还可以处理嵌套表单等。
我花了一些时间来掌握这个 fn,但当我掌握它后,我发现它非常有用!