2015-10-07 16 views
7

In un programma Clojure ho un array composto da mappe contenenti nomi ed e-mail di persone.Uguaglianza personalizzata in Clojure distinta

ad es.

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

vorrei rimuovere le voci duplicate in considerazione, ai fini del confronto, le coppie che hanno lo stesso e-mail per essere uguali. Nell'esempio sopra l'output sarebbe:

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

Qual è il modo migliore per ottenere questo in Clojure? C'è un modo per far sapere a distinti ciò che è uguale alla funzione da usare?

Grazie.

risposta

6

Questo lo farebbe: https://crossclj.info/fun/medley.core/distinct-by.html.

La funzione nel collegamento passa pigramente a ogni valore e memorizza tutto ciò che viene visto. Se il valore nel coll è già visto, non lo aggiunge.

Si potrebbe quindi chiamare questo come: (distinct-by #(% :email) maps), dove maps è il proprio vettore di mappe di persone.

+6

Poiché le parole chiave sono funzioni, un'invocazione più idiomatica sarebbe '(distinta-by: mappe e-mail) ' – Alex

9

ancora un altro modo per farlo, un po 'più idiomatica, credo:

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

uscita:

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

che è come funziona:

(group-by :email items) fa una mappa, la cui le chiavi sono email e i valori sono gruppi di record con questa email

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

quindi devi solo prendere i suoi valori (gruppi di record) e selezionare i primi da essi.

E un altro modo è quello di creare un ordinato set per e-mail, in modo che il trattamento di tutti i record con uguali email come uguali record:

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

uscita:

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

don' so davvero quale di loro è più idiomatico e ha una performance migliore. Ma scommetto sul primo.

0

Un distinct-by può essere facilmente implementati come

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

Per il caso esempio questo può essere usato come

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