过滤掉clojure中两个列表之间的键不匹配的元素

时间:2014-11-13 17:29:26

标签: clojure

我在clojure中有两个地图矢量

(def a [{:name "batman" :universe "DC" :email "batman@wayne.com"}
        {:name "flash" :universe "DC" :email "flash@speedfreak.com"}
        {:name "thor" :universe "MARVEL" :email "thor@asgard.com"}])

(def b [{:name "batman" :universe "DC" :email "batman@wayne.com"}
        {:name "flash" :universe "DC" :email "flash1@speedfreak.com"}
        {:name "thor" :universe "MARVEL" :email "thor@asgard.com"}
        {:name "riddler" :universe "DC" :email "riddler@whoami.com"}])

两个列表中的:name属性始终同步;即,batman中的a始终batman b。{/ p>

但是,我想要做的只是选择电子邮件不匹配的行。

(stuck-on-what-to-write-here)

=> ({:name "flash", :universe "DC", :email "flash1@speedfreak.com"})

如果我用

过滤掉行
(filter #(not (contains? (set (map :email a)) (:email %))) b)

它返回2行,其中一行flash因为它不匹配而另一行riddler因为..好吧,它不在a中,因此不匹配!

我需要做些什么来获得闪光而不是谜语?

3 个答案:

答案 0 :(得分:3)

(defn mismatch?
  "Returns true if there is any mismatch between corresponding items."
  [a b]
  (= (count (clojure.set/union (set a) (set b)))
     (max (count a) (count b))))

如果你想要一个特定的名字,你可以使用列表理解:

(defn get-mismatched-emails
  "Returns the name of any superheroes with inconsistent contact records."
  [a b]
  (for [i a j b
        :when (and (= (:name i) (:name j))
                   (not= (:email i) (:email j)))]
    (:name i)))

请注意,此函数效率较低,因为它必须比较两个列表之间的每对组合。只需将数据结构更改为地图地图即可:

{"batman" {:universe "DC" :email "batman@wayne.com"}
 "flash"  {:universe "DC" :email "flash@speedfreak.com"}
 "thor"   {:universe "MARVEL" :email "thor@asgard.com"}}

您宁愿轻松地将您想要的内容扩展到更大的数据集。

(for [name (clojure.set/union (set (keys a))
                              (set (keys b)))
      :when (detect-mismatched-data (a name) (b name))]
  name)

答案 1 :(得分:2)

一种可能的方法是在电子邮件中为您的过滤器添加第二个条件。我不知道galdre's answer以上的表现!

(def a-names (set (map :name a)))
(def a-emails (set (map :email a)))

(filter #(and
          (contains? a-names (:name %))
          (not (contains? a-emails (:email %)))) 
        b)

这将输出({:name "flash", :universe "DC", :email "flash1@speedfreak.com"})

也不是说我把(set (map :name a))放在了过滤器之外,所以它不需要循环来收集b中每个项目的名称。

答案 2 :(得分:0)

尝试:

(filter #(not (contains? (set (map :email b)) (:email %))) a)

返回:

({:universe "DC", :name "flash", :email "flash@speedfreak.com"})