2015-10-07 11 views
7

In einem Clojure-Programm habe ich ein Array von Karten mit Namen und E-Mails von Menschen zusammen.Benutzerdefinierte Gleichheit in Clojure distinct

z.B.

Ich möchte die doppelten Einträge entfernen, wobei zu Vergleichszwecken Paare berücksichtigt werden, die dieselbe E-Mail-Adresse haben. Im obigen Beispiel wäre die Ausgabe:

[ 
    { :name "John" :email "[email protected]" } 
    { :name "Batman" :email "[email protected]" } 
] 

Was ist der beste Weg, dies in Clojure zu erreichen? Gibt es eine Möglichkeit, distinct wissen zu lassen, welche Funktion gleich zu verwenden ist?

Danke.

Antwort

6

Dies würde es tun: https://crossclj.info/fun/medley.core/distinct-by.html.

Die Funktion in der Verbindung durchläuft jeden Wert träge und speichert alles, was es gesehen hat. Wenn der Wert inbereits angezeigt wird, wird er nicht hinzugefügt.

Sie könnten dies dann als (distinct-by #(% :email) maps) bezeichnen, wobei maps Ihr Vektor von People-Maps ist.

+6

Da Schlüsselwörter Funktionen sind, ein idiomatischer Aufruf wäre '(distinct-by: E-Mail-Karten) ' – Alex

9

noch eine andere Möglichkeit, es zu tun, ein bisschen mehr idiomatisch, ich denke:

(let [items [{ :name "John" :email "[email protected]" } 
      { :name "Batman" :email "[email protected]" } 
      { :name "John Doe" :email "[email protected]" }]] 
    (map first (vals (group-by :email items)))) 

Ausgang:

({:name "John", :email "[email protected]"} 
{:name "Batman", :email "[email protected]"}) 

das ist, wie es funktioniert:

(group-by :email items) eine Karte macht, deren Schlüssel sind E-Mails und Werte sind Gruppen von Datensätzen mit dieser E-Mail

{"[email protected]" [{:name "John", :email "[email protected]"} 
        {:name "John Doe", :email "[email protected]"}], 
"[email protected]" [{:name "Batman", :email "[email protected]"}]} 

dann müssen Sie nur seine Vals (Gruppen von Datensätzen) nehmen und wählen Sie erste aus ihnen.

Und eine andere Art und Weise ist eine per E-Mail-Set sortiert zu erstellen, damit es alle Datensätze mit der gleichen E-Mails als gleichwertige Aufzeichnungen behandeln:

(let [items [{ :name "John" :email "[email protected]" } 
      { :name "Batman" :email "[email protected]" } 
      { :name "John Doe" :email "[email protected]" }]] 
    (into (sorted-set-by #(compare (:email %1) (:email %2))) items)) 

Ausgang:

#{{:name "Batman", :email "[email protected]"} 
    {:name "John", :email "[email protected]"}} 

don‘ Ich weiß wirklich, welche von ihnen ist idiomatischer und hat eine bessere Leistung. Aber ich wette auf den ersten.

0

A distinct-by kann leicht als

(defn distinct-by [f coll] 
    (let [groups (group-by f coll)] 
    (map #(first (groups %)) (distinct (map f coll))))) 

Für den Beispielfall implementiert wird diese wie

verwendet werden kann
(distinct-by :email 
      [{:name "John" :email "[email protected]"} 
       {:name "Batman" :email "[email protected]"} 
       {:name "John Doe" :email "[email protected]"}])