2012-08-01 3 views
11

Was ist die idiomatische Art zu prüfen, ob ein Schlüssel auf einer Karte einen Wert hat? Zum Beispiel, wenn wir haben:Idiomatische Art zu prüfen, ob ein Schlüssel auf einer Karte einen Wert hat

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

Um herauszufinden, alle Karten mit: foo == 0, Ich mag:

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

Aber wenn ich will alle Karten mit finden: Bar == "Hallo", das Beste, was ich mir vorstellen kann, ist:

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

die ich nicht sehr gut lesbar finde. Gibt es eine bessere/idiomatische Art, es zu tun?

Antwort

4

Ich persönlich mag diese Art der Sache Refactoring eine klar benannte Funktion höherer Ordnung zu verwenden:

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

Nachteil ist, dass es Ihnen eine zusätzliche Funktionsdefinition gibt zu verwalten und zu pflegen, aber ich denke, die Eleganz/code Lesbarkeit ist es wert. Dieser Ansatz kann auch sehr leistungsfähig sein, wenn Sie das Prädikat mehrmals verwenden.

+0

Schöne Idee, ich mag es. Ganz nebenbei, im Code scheint Ihr letzter Aufruf nicht mit den 'seq-of-maps' übereinzustimmen, die Sie in der ersten Zeile definieren. –

+0

Whoops auf die Kopie/einfügen, behoben - danke! – mikera

9

Idiomatic ist subjektiv, aber ich tun würde

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

oder was Sie getan haben.

1

Ihr drittes Beispiel für das Übergeben einer anonymen Funktion zum Filtern scheint eine der idomischeren Methoden zum Auffinden von Karten mit einem bestimmten Wert zu sein. Ich fand es ziemlich leicht zu lesen.

3

clojure.set/index auch hier verwendet werden könnte

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

Wenn Sie wollen, dass es in einer Funktion wickeln kann

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

dann

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

beide arbeiten - Sie kann auch Karten mit mehreren Schlüssel/Werten unter Verwendung von Index anfordern, zB:

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

wählt alle Karten enthält foo 0 und Bar "baz"

+0

darüber nachgedacht, so scheint es, dass dies nicht das genaue Verhalten nicht erhalten Sie benötigen , da es einen konkreten Satz nur eindeutiger Karten zurückgibt, anstatt einen Stream, der möglicherweise Duplikate enthält. – 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"}) 

Wie Pepijn sagt, ich denke, idiomatischen Möglichkeiten, nach persönlichen Meinungen variieren. Ich verwende manchmal das Makro ->, um verschachtelte Klammern zu extrahieren.

0

Ihr Code sieht gut aus für mich. Andere mögliche Lösung sein kann for Makro zu verwenden, wie unten

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

gezeigt Einfach zero? mit (partial = "hi") wie folgt ersetzen:

=> (filter (comp (partial = "hi") :bar) seq-of-maps) 
Verwandte Themen