#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})
))
Вышесказанное основано на этом шаблоне проекта.