如何为clojure编写一个dissoc-in命令?

zca*_*ate 32 clojure

我正在寻找一个类似于assoc-in的函数但删除键而不是添加它:

(dissoc-in {:a {:b 0}} [:a :b])
;;=> {:a {}}
Run Code Online (Sandbox Code Playgroud)

我起床到这里:

(def m {:a {:b {:c 1}}})

(assoc  m :a (assoc (:a m) :b (dissoc (:b (:a m)) :c)))
;;=> {:a {:b {}}}
Run Code Online (Sandbox Code Playgroud)

但整个嵌套的东西都在弄乱我的脑袋

小智 89

我用update-in写这个:

(update-in {:a {:b 0 :c 1}} [:a] dissoc :b)
Run Code Online (Sandbox Code Playgroud)

=>

{:a {:c 1}}
Run Code Online (Sandbox Code Playgroud)


slo*_*oth 13

怎么样:

(defn dissoc-in
  "Dissociates an entry from a nested associative structure returning a new
  nested structure. keys is a sequence of keys. Any empty maps that result
  will not be present in the new structure."
  [m [k & ks :as keys]]
  (if ks
    (if-let [nextmap (get m k)]
      (let [newmap (dissoc-in nextmap ks)]
        (if (seq newmap)
          (assoc m k newmap)
          (dissoc m k)))
      m)
    (dissoc m k)))
Run Code Online (Sandbox Code Playgroud)

例:

(dissoc-in {:a {:b 0 :c 1}} [:a :b])
Run Code Online (Sandbox Code Playgroud)

结果:

{:a {:c 1}}
Run Code Online (Sandbox Code Playgroud)

dissoc-in曾经是其中的一部分clojure.contrib.core,现在也是其中的一部分core.incubator.


如果要保留空映射,可以稍微更改代码:

(defn dissoc-in
  [m [k & ks :as keys]]
  (if ks
    (if-let [nextmap (get m k)]
      (let [newmap (dissoc-in nextmap ks)]
        (assoc m k newmap))
      m)
    (dissoc m k)))
Run Code Online (Sandbox Code Playgroud)

例:

(dissoc-in {:a {:b {:c 0}}} [:a :b])
Run Code Online (Sandbox Code Playgroud)

结果:

{:a {}}
Run Code Online (Sandbox Code Playgroud)


Sym*_*rog 5

下面是一个基于 update-in 的通用解决方案:

(defn dissoc-in [m p]
  (if (get-in m p) 
    (update-in m
               (take (dec (count p)) p)
               dissoc (last p))
    m))
Run Code Online (Sandbox Code Playgroud)

  • 可以使用`(drop-last p)`代替`(take...)`。(或`butlast`,但可能不鼓励这样做。) (2认同)