У меня есть персистентная карта, которую я хочу отфильтровать. Что-то вроде этого:
(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)))
Нужно обойти все записи, но можно использовать постоянные карты Clojures:
(apply dissoc my-map (for [[k v] my-map :when (not= v 1)] k))
Согласно вашему комментарию Михалу Марчику:
(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}
Я не вижу, что вы много выиграете от этого по сравнению с версией Михала.
(into {} (filter #(-> % val (= 1)) {:a 1 :b 1 :c 2}))
Конечно, этот восстанавливает карту из последовательности записей карты, но нет никакого способа обойти это. Если вы собираетесь фильтровать записи по значению, вам придется просматривать их одну за другой, чтобы увидеть, какие значения соответствуют вашему предикату, а какие нет.
Обновлено (см. Комментарии ниже):
С недавно представленной функцией keep
, источник которой вы можете увидеть здесь (она должна работать нормально в Clojure 1.1, если вы хотите выполнить бэкпорт), это кажется хорошим способом решить эту проблему , если вы не используете 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}