Как отфильтровать постоянную карту в Clojure? - PullRequest
34 голосов
/ 02 мая 2010

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

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

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

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

Ответы [ 5 ]

48 голосов
/ 04 мая 2010

И еще один:

(let [m {:a 1 :b 2 :c 1}]
  (select-keys m (for [[k v] m :when (= v 1)] k)))
20 голосов
/ 02 мая 2010
(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}
3 голосов
/ 03 мая 2010

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

(apply dissoc my-map (for [[k v] my-map :when (not= v 1)] k))
3 голосов
/ 02 мая 2010

За ваш комментарий к Михалу Марчику:

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

Я не вижу, чтобы вы выиграли с этим по сравнению с версией Михала.

1 голос
/ 22 января 2012

Я попробовал себя в макросе для этого на основе версии 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}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...