Подтвердить что ты не робот

Как фильтровать постоянную карту в Clojure?

У меня есть постоянная карта, которую я хочу фильтровать. Что-то вроде этого:

(filter #(-> % val (= 1)) {:a 1 :b 1 :c 2})

Вышеизложенное обозначается как ([:a 1] [:b 1]) (ленивая последовательность записей карты). Однако я хочу получить {:a 1 :b 1}.

Как я могу отфильтровать карту, чтобы она оставалась картой, не перестраивая ее из последовательности записей карты?

4b9b3361

Ответ 1

И еще один:

(let [m {:a 1 :b 2 :c 1}]
  (select-keys m (for [[k v] m :when (= v 1)] k)))

Ответ 2

(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}

Ответ 3

За ваш комментарий к 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ł.

Ответ 4

Необходимо перемещать все записи, но может использовать постоянные карты Clojures:

(apply dissoc my-map (for [[k v] my-map :when (not= v 1)] k))

Ответ 5

Я попробовал себя на макросе для этого на основе версии 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}}