首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在clojure中按键找到映射集的交集?

如何在clojure中按键找到映射集的交集?
EN

Stack Overflow用户
提问于 2016-04-26 13:56:05
回答 2查看 1.1K关注 0票数 3

该函数接受一组数字向量,并返回它们的交集:

代码语言:javascript
运行
复制
(defn foo [& sets] (apply clojure.set/intersection (map #(set %) sets)))

例如:

代码语言:javascript
运行
复制
user=> (foo [1 2 3] [3 4 5 1] [33 3 3 1])
#{1 3}

如果输入集的元素是具有:id键作为映射的唯一属性的映射,那么如何实现相同的功能呢?

即。我正在编写一个函数foo2,它执行以下操作:

代码语言:javascript
运行
复制
user=> (foo2 [{:id 1 ...} {:id 2 ...} {:id 3 ...}] 
             [{:id 3 ...} {:id 4 ...} {:id 5 ...} {:id 1 ...}]
             [{:id 33 ...} {:id 3 ...} {:id 3 ...} {:id 1 ...}])
[{:id 1 ...} {:id 3 ...}]

有什么简单的方法吗?

或者是收集in、获取键的交集、然后在输入集中查找键的唯一方法?

是否有任何方法通过定义返回任意对象键的“键值”函数来“重载”集合函数(联合、交叉等),或者是否只能使用带有原语值的集合?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-04-26 15:32:58

编辑:我以前错误地假设具有相同:id的映射保证具有相同的键和值。如果允许他们有所不同,这是一个更好的答案:

代码语言:javascript
运行
复制
(defn intersect-by [key-fn data]
  (let [normalized (map (fn [map-data]
                          (->> map-data
                               (group-by key-fn)
                               (map (fn [[key val]]
                                      [key (apply merge val)]))
                               (into {})))
                        data)
        normalized-keys (map (comp set keys) normalized)
        intersection (apply clojure.set/intersection normalized-keys)
        merged-data (apply merge-with merge normalized)]
    (vals (select-keys merged-data intersection))))
#'user/foo

(def data [[{:id 1} {:id 2} {:id 3, :x 3}]
           [{:id 3, :y 4} {:id 4} {:id 5} {:id 1}]
           [{:id 33} {:id 3} {:id 3, :z 5} {:id 1}]])
#'user/data

(intersect-by :id data)
({:id 1} {:id 3, :x 3, :y 4, :z 5})

如果您假设两个具有相同:id的映射被保证具有相同的键和值,那么您的第一个foo函数已经这样做了:

代码语言:javascript
运行
复制
(def data [[{:id 1} {:id 2} {:id 3}]
           [{:id 3} {:id 4} {:id 5} {:id 1}]
           [{:id 33} {:id 3} {:id 3} {:id 1}]])
#'user/data

(defn foo [& sets] (apply clojure.set/intersection (map #(set %) sets)))
#'user/foo

(apply foo data)
#{{:id 1} {:id 3}}
票数 2
EN

Stack Overflow用户

发布于 2016-04-26 14:13:00

在将每个向量转换为由clojure.set/intersection设置的排序后,我将做几乎相同的操作::id

代码语言:javascript
运行
复制
user> (defn intersect [& colls]
        (apply clojure.set/intersection
               (map #(apply sorted-set-by
                            (fn [{id1 :id} {id2 :id}] (compare id1 id2))
                            %)
                    colls)))
#'user/intersect

user> (intersect [{:id 1} {:id 2} {:id 3}] 
                 [{:id 3} {:id 4} {:id 5} {:id 1}]
                 [{:id 33} {:id 3} {:id 3} {:id 1}])
#{{:id 1} {:id 3}}

注意,这种方法将使用相同的:id值处理不同的映射,但我想这正是您所需要的。顺便说一句,sorted-set正是您想要的“重载”设置函数的方式。

您可以使一个助手函数看起来更通用:

代码语言:javascript
运行
复制
user> (defn key-comparator [key]
        #(compare (key %1) (key %2)))
#'user/key-comparator

user> (defn intersect [& colls]
        (apply clojure.set/intersection
               (map #(apply sorted-set-by (key-comparator :id) %)
                    colls)))
#'user/intersect

user> (intersect [{:id 1} {:id 2} {:id 3 :x 1}] 
                 [{:id 3 :x 10} {:id 4} {:id 5} {:id 1}]
                 [{:id 33} {:id 3 :x 33} {:id 3 :x 2} {:id 1}])
#{{:id 1} {:id 3, :x 33}}

另一个变体是过滤第一个coll中的所有项,其id存在于所有其他coll中:

代码语言:javascript
运行
复制
user> (defn intersect-2 [c & cs]
        (if (empty? cs) c
            (filter (fn [{id :id :as itm}]
                      (every? (partial some #(when (= id (:id %)) %))
                              cs))
                    c)))
#'user/intersect-2

user> (intersect-2 [{:id 1} {:id 2} {:id 3 :x 1}] 
                   [{:id 3 :x 10} {:id 4} {:id 5} {:id 1}]
                   [{:id 33} {:id 3 :x 33} {:id 3 :x 2} {:id 1}])
({:id 1} {:id 3, :x 1})
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36867070

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档