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

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

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

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

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

35
задан Alex B 2 May 2010 в 15:08
поделиться

4 ответа

И еще одно:

(let [m {:a 1 :b 2 :c 1}]
  (select-keys m (for [[k v] m :when (= v 1)] k)))
49
ответ дан 27 November 2019 в 06:52
поделиться

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

(apply dissoc my-map (for [[k v] my-map :when (not= v 1)] k))
3
ответ дан 27 November 2019 в 06:52
поделиться

Согласно вашему комментарию Михалу Марчику:

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

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

3
ответ дан 27 November 2019 в 06:52
поделиться
(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}
20
ответ дан 27 November 2019 в 06:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: