объединение карт, но некоторые значения равны нулю

#clojure #hashmap

Вопрос:

Итак, у меня есть две карты, которые могут быть любыми, и я хочу объединить их, но не включать значения, которые равны нулю. Допустим, у меня есть:

 (def x {:a "A" :c 5 :d "D"})
(def y {:a 1 :b 2 :c nil})
 

Я хочу закончить с

 {:a 1 :b 2 :c 5 :d "D"}
 

Я получаю неправильное значение для :c, если я просто сливаюсь, как (объединить x y), но {:c nil} есть. У меня нет контроля над тем, какие две карты входят. Любая помощь будет оценена по достоинству

Комментарии:

1. Кроме того, если x бы это было {:a nil} и y было, был {:b 2} бы желаемый результат {:a nil :b 2} или {:b 2} ? Существует разница между фильтрацией всех нулевых значений и предотвращением nil переопределения значений на более поздних картах значениями на более ранних картах. Различия можно увидеть в двух уже данных ответах.

Ответ №1:

Если вы хотите объединить хэш — карты таким образом, чтобы nil значения не переопределяли nil неценности, вы можете использовать merge-with :

 dev=> (def x {:a "A" :c 5 :d "D"})
#'dev/x
dev=> (def y {:a 1 :b 2 :c nil})
#'dev/y
dev=> (merge-with (fn [a b] (if (some? b) b a)) x y)
{:a 1, :c 5, :d "D", :b 2}
dev=> 
 

some? возвращает true , если его аргумент не имеет nil значения.

Ответ №2:

Использование into со вторым аргументом, являющимся фильтрующим преобразователем, приводит к фрагменту кода, который является достаточно кратким и читаемым:

 (def x {:a "A" :c 5 :d "D"})
(def y {:a 1 :b 2 :c nil})

(into x (filter (comp some? val)) y)
;; => {:a 1, :c 5, :d "D", :b 2}
 

Требуются только незначительные изменения, чтобы он также удалил nils с первой карты, если вам это нужно:

 (def x {:a "A" :c 5 :d "D" :e nil :f nil})
(def y {:a 1 :b 2 :c nil})

(into {} (comp cat (filter (comp some? val))) [x y])
;; => {:a 1, :c 5, :d "D", :b 2}
 

Комментарии:

1. Я бы, вероятно, использовал val вместо second этого здесь, чтобы дать понять, что он работает с записями на карте, но в остальном это действительно отличное решение проблемы!

2. Вместо (filter (comp some? val)) этого более буквальная кодировка намерения «удалить нулевые значения» может быть (remove (comp nil? val))

3. Спасибо всем! Решение было идеальным! (в {} (комп кот (удалить (комп ноль? вал))) [x y])

Ответ №3:

Вы можете легко удалить записи карты, которые содержат nil подобное:

 (ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [schema.core :as s]
  ))

(defn remove-nil-vals
  [m]
  (into {}
        (for [[k v] m
              :when (not (nil? v))]
          [k v])))

(dotest
  (let [x              {:a "A" :c 5 :d "D"}
        y              {:a 1 :b 2 :c nil}
        all-maps       [x y]
        merged-no-nils (into {}
                             (for [m all-maps]
                               (remove-nil-vals m)))]

    (is= (remove-nil-vals x)  {:a "A" :c 5 :d "D"})
    (is= (remove-nil-vals y)  {:a 1 :b 2})
    (is= merged-no-nils       {:a 1 :c 5 :d "D" :b 2})

  ))
 

Вышесказанное основано на этом шаблоне проекта.