43 votes

Comment filtrer une carte persistante dans Clojure?

J'ai une carte persistante que je veux filtrer. Quelque chose comme ça:

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

Ce qui précède apparaît comme ([:a 1] [:b 1]) (une séquence paresseuse d'entrées de carte). Cependant, je veux être {:a 1 :b 1} .

Comment puis-je filtrer une carte pour qu'elle reste une carte sans avoir à la reconstruire à partir d'une séquence d'entrées de carte?

51voto

kotarak Points 11177

Et un autre:

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

21voto

Michał Marczyk Points 54179
(into {} (filter #(-> % val (= 1)) {:a 1 :b 1 :c 2}))

Bien sûr, ce n' reconstruire la carte à partir d'une séquence de la carte des entrées, mais il n'y a pas moyen de contourner cela. Si vous allez pour filtrer les entrées en valeur, vous allez avoir à passer par un par un pour voir les valeurs qui correspondent à vos prédicat et qui ne le sont pas.

Mis à jour (voir les commentaires ci-dessous):

Avec l'introduction du nouveau keep de la fonction, la source de laquelle vous pouvez voir ici (devrait fonctionner très bien en Clojure 1.1 si vous voulez backport), cela semble être une belle façon d'aller à ce sujet si vous n'utilisez pas nil clé:

(let [m {:a 1 :b 1 :c 2}]
  (apply dissoc m (keep #(-> % val (= 1) (if nil (key %))) m)))
; => {:a 1, :b 1}

Aussi, si vous ne vous en fait voir un ralentissement lié à la reconstruction de votre carte, vous pouvez utiliser un transitoire de la carte lors de l'étape de reconstruction:

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

3voto

Brian Carper Points 40078

Selon votre commentaire à 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}
 

Je ne vois pas que vous allez gagner beaucoup avec cette version de Michał.

3voto

Jürgen Hötzel Points 6613

Vous devez parcourir toutes les entrées, mais vous pouvez tirer parti des cartes persistantes de Clojures:

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

1voto

Benjamin Peter Points 123

Je me suis essayé sur macro pour cela basé sur la version de kotarak. C'est ma première macro qui fait quelque chose d'utile, alors soyez indulgents et bienvenue.

 (defmacro filter-map [bindings pred m]
  `(select-keys ~m
    (for [~bindings ~m
      :when ~pred]
      ~(first bindings)
    )
  )
)
 

Exemple

 user=> (filter-map [key val] (even? (:attr val)) {:a {:attr 2} :b {:attr 3} :c {:attr 4}})
{:c {:attr 4}, :a {:attr 2}}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X