2010-05-02 2 views
31

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

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

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

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

ответ

45

И еще одно:

(let [m {:a 1 :b 2 :c 1}] 
    (select-keys m (for [[k v] m :when (= v 1)] k))) 
17
(into {} (filter #(-> % val (= 1)) {:a 1 :b 1 :c 2})) 

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

обновился (см комментарии ниже):

С недавно введенной keep функции, источник которого вы можете увидеть here (должен работать только штрафом в 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} 
+0

Ну, * теоретически * вы можете иметь его фильтруется стоимости без восстановления, возвращая карту с помощью клавиш dissoc-е изд, которые соответствуют значениям несовпадающих. Я надеялся, что есть способ, поддерживаемый языками. –

+0

Хорошо, я понимаю, что вы имеете в виду. Я добавлю два способа сделать это за секунду, но обратите внимание, что вы вряд ли увидите большой выигрыш в отделе производительности (если у вас нет действительно огромной карты, и вы будете только разобрать небольшую количество ключей). –

+0

Хм, на самом деле это не так много «двух способов сделать это» как «один из способов сделать это и один способ меньше беспокоиться о перестройке». Не то чтобы вам очень нужно было волноваться. :-) –

3

За свой комментарий к Михал 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} 

Я не вижу, что вы собираетесь получить много с этой VS. версии Michal в.

3

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

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

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