Clojure中distinct的自定义相等性

14
在一个Clojure程序中,我有一个由包含人名和电子邮件的映射组成的数组。
例如:
[
    { :name "John" :email "john@gmail.com" }  
    { :name "Batman" :email "batman@gmail.com" }  
    { :name "John Doe" :email "john@gmail.com" }  
 ] 

我希望能够删除重复的条目,对于比较目的而言,将具有相同电子邮件的一对视为相等。在上面的示例中,输出将是:

[
    { :name "John" :email "john@gmail.com" }  
    { :name "Batman" :email "batman@gmail.com" }  
 ] 

在Clojure中实现这一目标的最佳方法是什么?有没有一种方法让“distinct”知道使用哪个等于函数?

谢谢。

3个回答

19

另一种方式来做,更加符合惯用法,我觉得是这样:

(let [items [{ :name "John" :email "john@gmail.com" }  
             { :name "Batman" :email "batman@gmail.com" }  
             { :name "John Doe" :email "john@gmail.com" }]]
  (map first (vals (group-by :email items))))

输出:

({:name "John", :email "john@gmail.com"} 
 {:name "Batman", :email "batman@gmail.com"})

它的工作原理如下:

(group-by :email items) 创建一个映射,其中键是电子邮件,值是具有此电子邮件的记录组。

{"john@gmail.com" [{:name "John", :email "john@gmail.com"} 
                   {:name "John Doe", :email "john@gmail.com"}], 
 "batman@gmail.com" [{:name "Batman", :email "batman@gmail.com"}]}

那么你只需要获取它的值(记录的集合),并从中选择第一个。

另一种方法是按电子邮件创建一个排序集合,这样它将把所有具有相同电子邮件的记录视为相等的记录:

(let [items [{ :name "John" :email "john@gmail.com" }  
             { :name "Batman" :email "batman@gmail.com" }  
             { :name "John Doe" :email "john@gmail.com" }]]
  (into (sorted-set-by #(compare (:email %1) (:email %2))) items))

输出:

#{{:name "Batman", :email "batman@gmail.com"} 
  {:name "John", :email "john@gmail.com"}}

我不太确定哪个更符合惯用语并具有更好的性能,但我打赌第一个会更好。


8
这样做就可以了:https://crossclj.info/fun/medley.core/distinct-by.html。该链接中的函数会惰性地遍历每个值并将其看到的所有内容存储起来。如果在coll中的值已经被看到过,则不添加它。你可以这样调用它:(distinct-by #(% :email) maps),其中maps是您的人员映射向量。

7
由于关键字是函数,更习惯用的调用方式是(distinct-by :email maps) - Alex

3

distinct-by可以很容易地实现如下:

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

对于这个示例案例,可以像这样使用:

(distinct-by :email
             [{:name "John" :email "john@gmail.com"}  
              {:name "Batman" :email "batman@gmail.com"}  
              {:name "John Doe" :email "john@gmail.com"}])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接