Как написать команду disoc-in для clojure?
Я хочу написать функцию, похожую на ассоциацию, но удаляет ключи, а не добавляя их:
(dissoc-in {:a {:b 0}} [:a :b])
;;=> {:a {}}
Я встал здесь:
(def m {:a {:b {:c 1}}})
(assoc m :a (assoc (:a m) :b (dissoc (:b (:a m)) :c)))
;;=> {:a {:b {}}}
но вся вложенная вещь возится с моей головой
Ответы
Ответ 1
Как насчет:
(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)))
Пример:
(dissoc-in {:a {:b 0 :c 1}} [:a :b])
Результат:
{:a {:c 1}}
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)))
Пример:
(dissoc-in {:a {:b {:c 0}}} [:a :b])
Результат:
{:a {}}
Ответ 2
Я пишу это с помощью update-in:
(update-in {:a {:b 0 :c 1}} [:a] dissoc :b)
= >
{:a {:c 1}}
Ответ 3
Будучи вдохновлен кодом Доминика. Я написал более сжатую версию
(defn dissoc-in
[m [k & ks]]
(if-not ks
(dissoc m k)
(assoc m k (dissoc-in (m k) ks))))
(dissoc-in {:a {:b {:c 1}}} [:a :b :c]) ;; => {:a {:b {}}}
Другая версия dissoc-in2 рекурсивно удаляет пустые карты
(defn dissoc-in2
[m [k & ks]]
(if-not ks
(dissoc m k)
(let [nm (dissoc-in2 (m k) ks)]
(cond (empty? nm) (dissoc m k)
:else (assoc m k nm)))))
(ut/dissoc-in {:a {:b {:c 3}}} [:a :b :c])
;;; => {:a {:b {}}}
(ut/dissoc-in2 {:a {:b {:c 3}}} [:a :b :c])
;;=> {}
Ответ 4
Вот общее решение на основе update-in:
(defn dissoc-in [m p]
(if (get-in m p)
(update-in m
(take (dec (count p)) p)
dissoc (last p))
m))
Ответ 5
Не нужно писать, clojure.core.incubator уже имеет dissoc-in
:
=> (dissoc-in {:children [{:name "Billy" :age 5}]} [:children 0 :age])
{:children [{:name "Billy"}]}
Ответ 6
Я бы рекомендовал использовать dissoc-in
из смешанной библиотеки.
Вот код с версии 0.7.0:
(defn dissoc-in
"Dissociate a value in a nested assocative structure, identified by a sequence
of keys. Any collections left empty by the operation will be dissociated from
their containing structures."
[m ks]
(if-let [[k & ks] (seq ks)]
(if (seq ks)
(let [v (dissoc-in (get m k) ks)]
(if (empty? v)
(dissoc m k)
(assoc m k v)))
(dissoc m k))
m))
Здесь ссылка на исходный код disoc-in в главном хозяине.
Ответ 7
(defn dissoc-in [m ks]
(let [parent-path (butlast ks)
leaf-key (last ks)]
(if (= (count ks) 1)
(dissoc m leaf-key)
(update-in m parent-path dissoc leaf-key))))