2012-08-01 8 views
11

¿Cuál es la forma idiomática de verificar si una clave en un mapa tiene un valor? Por ejemplo si tenemos:Forma idiomática de comprobar si una clave en un mapa tiene un valor

=> (def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}]) 

Para conocer todos los mapas con: foo == 0, me gusta:

=> (filter (comp zero? :foo) seq-of-maps) 
({:foo 0, :bar "baz"}) 

Pero si quiero encontrar todos los mapas con: bar == "hola", lo mejor que puedo pensar es:

=> (filter #(= (:bar %) "hi") seq-of-maps) 
({:foo 1, :bar "hi"}) 

que no me parecen muy legibles. ¿Hay una forma mejor/más idiomática de hacerlo?

Respuesta

4

personalmente me gusta refactorización este tipo de cosas a utilizar una mayor función de orden claramente con un nombre:

(def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}]) 

(defn has-value [key value] 
    "Returns a predicate that tests whether a map contains a specific value" 
    (fn [m] 
    (= value (m key)))) 

(filter (has-value :bar "hi") seq-of-maps) 
=> ({:foo 1, :bar "hi"}) 

Lo malo es que le da una definición de función extra para gestionar y mantener, pero creo que la elegancia/Código la legibilidad lo vale. Este enfoque también puede ser muy eficiente desde una perspectiva de rendimiento si reutiliza el predicado muchas veces.

+0

Buena idea, me gusta. Solo como un lado, en el código, su llamada final no parece coincidir con el 'seq-of-maps' que define en la primera línea. –

+0

Whoops en el copiar/pegar, lo arregló, gracias! – mikera

9

idiomático es subjetiva, pero me gustaría hacer

=> (filter (comp #{"hi"} :bar) seq-of-maps) 

o lo que hizo.

1

Su tercer ejemplo de pasar una función anónima para filtrar parece ser uno de los métodos más idóneos para encontrar mapas con un valor determinado. Me pareció bastante fácil de leer.

3

clojure.set/index también podría ser utilizado aquí

((index seq-of-maps [:foo]) {:foo 0}) 
((index seq-of-maps [:bar]) {:bar "hi"}) 

Si se desea se puede envolver en una función

(defn select-maps [xrel m] 
    ((index xrel (keys m)) m)) 

continuación

(select-maps seq-of-maps {:foo 0}) 
(select-maps seq-of-maps {:bar "hi"}) 

tanto trabajo - usted también puede solicitar mapas con varias claves/valores usando el índice, por ejemplo:

(select-maps seq-of-maps {:foo 0 :bar "baz"}) 

selecciona todos los mapas que contienen foo 0 y "Baz" barra

+0

Pensando en esto, parece que esto no consigue el comportamiento exacto que necesita , ya que devolverá un conjunto concreto de mapas únicos, en lugar de una secuencia que contenga duplicados. – ChrisR

2
user> (def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}]) 
#'user/seq-of-maps 
user> (filter #(-> % :bar (= "hi")) seq-of-maps) 
({:foo 1, :bar "hi"}) 

Como dice Pepjin, creo formas idiomáticas varían de acuerdo con las opiniones personales. A veces uso la macro -> para extraer paréntesis anidados.

0

Tu código me parece bien. Otra posible solución puede ser utilizar for macro como se muestra a continuación

(for [m seq-of-maps 
     :let [v (:bar m)] 
     :when (= v "hi")] 
    m) 
0

Basta con sustituir zero? con (partial = "hi") así:

=> (filter (comp (partial = "hi") :bar) seq-of-maps) 
Cuestiones relacionadas