Ответ 1
И еще один:
(let [m {:a 1 :b 2 :c 1}]
(select-keys m (for [[k v] m :when (= v 1)] k)))
У меня есть постоянная карта, которую я хочу фильтровать. Что-то вроде этого:
(filter #(-> % val (= 1)) {:a 1 :b 1 :c 2})
Вышеизложенное обозначается как ([:a 1] [:b 1])
(ленивая последовательность записей карты). Однако я хочу получить {:a 1 :b 1}
.
Как я могу отфильтровать карту, чтобы она оставалась картой, не перестраивая ее из последовательности записей карты?
И еще один:
(let [m {:a 1 :b 2 :c 1}]
(select-keys m (for [[k v] m :when (= v 1)] k)))
(into {} (filter #(-> % val (= 1)) {:a 1 :b 1 :c 2}))
Конечно, это перестраивает карту из последовательности записей карты, но ее нет. Если вы собираетесь фильтровать записи по значению, вам придется проходить через них один за другим, чтобы увидеть, какие значения соответствуют вашему предикату, а какие нет.
Обновлено (см. комментарии ниже):
С недавно введенной функцией keep
, источник которой вы можете увидеть здесь (должен отлично работать в Clojure 1.1 if вы хотите выполнить резервное копирование), это выглядит как хороший способ сделать это, если вы не используете nil
в качестве ключа:
(let [m {:a 1 :b 1 :c 2}]
(apply dissoc m (keep #(-> % val (= 1) (if nil (key %))) m)))
; => {:a 1, :b 1}
Кроме того, если вы действительно видите замедление, связанное с восстановлением вашей карты, вы можете использовать переходную карту на этапе восстановления:
(persistent! (loop [m (transient {})
to-go (seq [[:a 1] [:b 2]])]
(if to-go
(recur (apply assoc! m (first to-go))
(next to-go))
m)))
; => {:a 1, :b 2}
За ваш комментарий к Michał Marczyk:
(defn filter* [f map]
(reduce (fn [m [k v :as x]]
(if-not (f x)
(dissoc m k)
m))
map map))
user> (filter* #(-> % val (= 1)) {:a 1 :b 1 :c 2})
{:a 1, :b 1}
Я не вижу, что вы многое выиграете с этой версией Michał.
Необходимо перемещать все записи, но может использовать постоянные карты Clojures:
(apply dissoc my-map (for [[k v] my-map :when (not= v 1)] k))
Я попробовал себя на макросе для этого на основе версии kotarak. Его первый макрос делает что-то полезное, поэтому, пожалуйста, несите меня и комментарии приветствуются.
(defmacro filter-map [bindings pred m]
`(select-keys ~m
(for [~bindings ~m
:when ~pred]
~(first bindings)
)
)
)
Пример
user=> (filter-map [key val] (even? (:attr val)) {:a {:attr 2} :b {:attr 3} :c {:attr 4}})
{:c {:attr 4}, :a {:attr 2}}