2012-08-01 11 views
11

Jaki jest idiomatyczny sposób sprawdzania, czy klucz na mapie ma wartość? Na przykład, jeśli mamy:Idiomatyczny sposób sprawdzania, czy klucz na mapie ma wartość

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

Aby dowiedzieć się wszystkich mapach z: foo == 0, lubię:

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

Ale jeśli chcę znaleźć wszystkie mapy z: bar == „hi”, najlepiej, że mogę myśleć to:

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

który nie znajdę bardzo czytelny. Czy istnieje lepszy/bardziej idiomatyczny sposób robienia tego?

Odpowiedz

4

ja osobiście jak refactoring tego typu rzeczy używać wyraźnie nazwie funkcja wyższego rzędu:

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

Minusem jest to, że daje dodatkową definicję funkcji do zarządzania i utrzymania, ale myślę, że elegancja/code czytelność jest tego warta. Takie podejście może być również bardzo wydajne z perspektywy wydajności, jeśli wielokrotnie używasz predykatu.

+0

Niezły pomysł, podoba mi się. Na marginesie, w kodzie, twoje ostatnie połączenie nie pasuje do "seq-of-map", które zdefiniowałeś w pierwszym wierszu. –

+0

Whoops na kopiuj/wklej, naprawił to- dzięki! – mikera

9

Idiomatic jest subiektywna, ale zrobiłbym

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

lub co zrobiłeś.

1

Twój trzeci przykład przekazania anonimowej funkcji do filtrowania wydaje się być jedną z bardziej idomatycznych metod wyszukiwania map o danej wartości. Znalazłem to dość łatwe do odczytania.

3

clojure.set/index może być również stosowany tutaj

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

Jeśli chcesz możesz owinąć go w funkcji

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

następnie

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

zarówno praca - ty może również zażądać map z wieloma kluczami/wartościami za pomocą indeksu, np .:

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

wybiera wszystkie mapy zawierające foo 0 i bar "baz"

+0

Myśląc o tym, wydaje się, że to nie uzyskać dokładne zachowanie, które wymagają , ponieważ zwróci konkretny zestaw tylko unikatowych map, a nie strumień potencjalnie zawierający duplikaty. – 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"}) 

Jak mówi Pepijn, myślę idiomatyczne sposoby różnią się w zależności od osobistych poglądów. Czasami używam makra ->, aby wyodrębnić zagnieżdżone nawiasy.

0

Twój kod wygląda dobrze dla mnie. Inne możliwe rozwiązanie może być użycie for makro, jak pokazano poniżej

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

Wystarczy wymienić zero? z (partial = "hi") tak:

=> (filter (comp (partial = "hi") :bar) seq-of-maps) 
Powiązane problemy