Удалить нулевые значения с карты? - PullRequest
27 голосов
/ 15 октября 2010

У меня есть карта 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}

Ответы [ 9 ]

55 голосов
/ 15 октября 2010

ваш для понимания списка возвращает СПИСОК карт, поэтому вам нужно ПРИМЕНИТЬ этот список к функции слияния в качестве необязательных аргументов:

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}  

Не удалять false значения:

user> (into {} (remove (comp nil? second) record))
{:a 1, :b false}  

Использование disoc для разрешения постоянных данныхобщий доступ вместо создания новой карты:

user> (apply dissoc                                                                                            
       record                                                                                                  
       (for [[k v] record :when (nil? v)] k))
{:a 1, :b 2}  
7 голосов
/ 05 марта 2014

Вот тот, который работает на вложенных картах:

(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)))
5 голосов
/ 11 декабря 2015

Вариант ответа @ 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)))

До точки @ 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 голосов
/ 15 октября 2010

Вы можете поместить его в карту:

(into {} (remove (fn [[k v]] (nil? v)) {:a 1 :b 2 :c nil}))
=> {:a 1 :b 2}
4 голосов
/ 15 февраля 2014

Решение Jürgen Hötzel, улучшенное для устранения проблемы «ноль / ложь»

(into {} (filter #(not (nil? (val %))) {:a true :b false :c nil}))

Немного более короткая версия решения @thnetos

(into {} (remove #(nil? (val %)) {:a true :b false :c nil}))
2 голосов
/ 18 августа 2016

Reducer-KV также может быть использован для удаления ключей

(reduce-kv (fn [m key value]
                (if (nil? value)
                  (dissoc m key)
                  m))
            {:test nil, :test1 "hello"}
            {:test nil, :test1 "hello"})
2 голосов
/ 15 октября 2010

Несмотря на то, что подход Юргена (фильтра второй записи) получил мой голос за Niftiest Clojure Trick, я решил бросить другой путь, на этот раз используя select-keys:

user> (select-keys record (for [[k v] record :when (not (nil? v))] k))
{:b 2, :a 1}
1 голос
/ 14 апреля 2015

Вот тот, который работает на картах и ​​векторах:

(defn compact
  [coll]
  (cond
    (vector? coll) (into [] (filter (complement nil?) coll))
    (map? coll) (into {} (filter (comp not nil? second) coll))))
1 голос
/ 15 октября 2010

Вы можете использовать уменьшение.

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}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...