Идиоматический способ проверки того, имеет ли ключ на карте значение
Каков идиоматический способ проверки того, имеет ли ключ на карте значение? Например, если у нас есть:
=> (def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}])
Чтобы узнать все карты с помощью: foo == 0, мне нравится:
=> (filter (comp zero? :foo) seq-of-maps)
({:foo 0, :bar "baz"})
Но если я хочу найти все карты с: bar == "hi", лучшее, что я могу придумать:
=> (filter #(= (:bar %) "hi") seq-of-maps)
({:foo 1, :bar "hi"})
который я не считаю очень удобочитаемым. Есть ли лучший/более идиоматический способ сделать это?
Ответы
Ответ 1
Мне лично нравится рефакторинг такого типа, чтобы использовать четко названную функцию более высокого порядка:
(def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}])
(defn has-value [key value]
"Returns a predicate that tests whether a map contains a specific value"
(fn [m]
(= value (m key))))
(filter (has-value :bar "hi") seq-of-maps)
=> ({:foo 1, :bar "hi"})
Даунсайд - это то, что он дает вам дополнительное определение функции для управления и поддержки, но я думаю, что удобство в отношении элегантности/кода того стоит. Этот подход также может быть очень эффективным с точки зрения производительности, если вы повторно используете предикат много раз.
Ответ 2
Идиоматик субъективен, но я бы сделал
=> (filter (comp #{"hi"} :bar) seq-of-maps)
или что вы сделали.
Ответ 3
clojure.set/index можно также использовать здесь
((index seq-of-maps [:foo]) {:foo 0})
((index seq-of-maps [:bar]) {:bar "hi"})
Если вы хотите, вы можете обернуть его в функцию
(defn select-maps [xrel m]
((index xrel (keys m)) m))
затем
(select-maps seq-of-maps {:foo 0})
(select-maps seq-of-maps {:bar "hi"})
оба работают - вы также можете запросить карты с несколькими ключами/значениями, используя индекс, например:
(select-maps seq-of-maps {:foo 0 :bar "baz"})
выбирает все карты, содержащие foo 0 и bar "baz"
Ответ 4
user> (def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}])
#'user/seq-of-maps
user> (filter #(-> % :bar (= "hi")) seq-of-maps)
({:foo 1, :bar "hi"})
Как говорит Пепийн, я думаю, что идиоматические способы варьируются в зависимости от личных мнений.
Я иногда использую макрос ->
для извлечения вложенных круглых скобок.
Ответ 5
Ваш третий пример передачи анонимной функции для фильтрации кажется одним из более идоматических методов поиска карт с заданным значением. Мне было легко читать.
Ответ 6
Ваш код выглядит хорошо для меня. Другим возможным решением может быть использование макроса for
, как показано ниже.
(for [m seq-of-maps
:let [v (:bar m)]
:when (= v "hi")]
m)
Ответ 7
Просто замените zero?
на (partial = "hi")
следующим образом:
=> (filter (comp (partial = "hi") :bar) seq-of-maps)