Удалить значения nil с карты?
У меня есть карта Clojure, которая может содержать значения, равные нулю, и я пытаюсь написать функцию для их удаления без особого успеха (я новичок в этом).
например:.
(def record {:a 1 :b 2 :c nil})
(merge (for [[k v] record :when (not (nil? v))] {k v}))
В результате получается последовательность карт, чего я не ожидал от слияния:
({:a 1} {:b 2})
Я бы хотел:
{:a 1, :b 2}
Ответы
Ответ 1
ваше понимание списка возвращает LIST карт, поэтому вам необходимо применить этот список к функции слияния как необязательные аргументы:
user> (apply merge (for [[k v] record :when (not (nil? v))] {k v}))
{:b 2, :a 1}
Более сжатое решение путем фильтрации карты в виде последовательности и объединения в карту:
user> (into {} (filter second record))
{:a 1, :b 2}
Не удалять ложные значения:
user> (into {} (remove (comp nil? second) record))
{:a 1, :b false}
Использование dissoc для обеспечения постоянного совместного использования данных вместо создания всей новой карты:
user> (apply dissoc
record
(for [[k v] record :when (nil? v)] k))
{:a 1, :b 2}
Ответ 2
Вот один, который работает на вложенных картах:
(defn remove-nils
[m]
(let [f (fn [[k v]] (when v [k v]))]
(postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))
Ответ 3
Вы можете перемалывать его на карту:
(into {} (remove (fn [[k v]] (nil? v)) {:a 1 :b 2 :c nil}))
=> {:a 1 :b 2}
Ответ 4
Вариант ответа @Eelco:
(defn remove-nils [m]
(let [f (fn [x]
(if (map? x)
(let [kvs (filter (comp not nil? second) x)]
(if (empty? kvs) nil (into {} kvs)))
x))]
(clojure.walk/postwalk f m)))
To @broma0 указывает, что он удаляет любые пустые карты.
user> (def m {:a nil, :b 1, :c {:z 4, :y 5, :x nil}, :d {:w nil, :v nil}})
user> (remove-nils m)
{:b 1, :c {:z 4, :y 5}}
user> (remove-nils {})
nil
Ответ 5
Решение Jürgen Hötzel уточнено, чтобы исправить ошибку nil/false
(into {} (filter #(not (nil? (val %))) {:a true :b false :c nil}))
Немного короче версия решения @thnetos
(into {} (remove #(nil? (val %)) {:a true :b false :c nil}))
Ответ 6
Несмотря на то, что подход Юргена (фильтр второй записи) получает мой голос за Niftiest Clojure Trick, я думал, что буду бросать другой путь туда, на этот раз используя select-keys
:
user> (select-keys record (for [[k v] record :when (not (nil? v))] k))
{:b 2, :a 1}
Ответ 7
Вы можете использовать сокращение.
user> (reduce (fn [m [k v]] (if (nil? v) m (assoc m k v))) {} record)
{:b 2, :a 1}
Если по какой-то причине вы хотите сохранить порядок (что обычно не важно на карте), вы можете использовать dissoc
.
user> (reduce (fn [m [k v]] (if (nil? v) (dissoc m k) m)) record record)
{:a 1, :b 2}
Ответ 8
Здесь тот, который работает на картах и векторах:
(defn compact
[coll]
(cond
(vector? coll) (into [] (filter (complement nil?) coll))
(map? coll) (into {} (filter (comp not nil? second) coll))))
Ответ 9
уменьшить-kv также можно использовать для удаления ключей
(reduce-kv (fn [m key value]
(if (nil? value)
(dissoc m key)
m))
{:test nil, :test1 "hello"}
{:test nil, :test1 "hello"})