你怎么在clojure中通过键找到交集的地图集?

时间:2016-04-26 13:56:06

标签: clojure

此函数采用一组数字向量并返回它们的交集:

var width = $(".expand").width();
$('.button').click(function() {
  $(this).text(function(i, text) {
    return text === "Show Details" ? "Hide Details" : "Show Details";
  })
  var toggleWidth = $(".expand").width() == 0 ? "60%" : "0";
  $(".expand").animate({
    width: toggleWidth
  }, {
    duration: 200,
    specialEasing: {
      width: 'linear'
    }
  });
});

例如

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

如果输入集的元素是带有user=> (foo [1 2 3] [3 4 5 1] [33 3 3 1]) #{1 3} 键的地图,可以用作地图的唯一属性,那么如何实现同样的目的呢?

即。我试图编写一个执行此操作的函数foo2:

:id

有一种简单的方法吗?

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

有没有办法过载'设置函数(并集,交集等),通过定义键值'返回任意对象的键的函数,还是只能使用具有原始值的集合?

2 个答案:

答案 0 :(得分:2)

我会做的几乎相同:clojure.set/intersection,将每个向量转换为:id的排序集后:

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正好是超载"设置你要问的功能。

你可以创建一个帮助函数,使它看起来更通用:

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中出现:

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

答案 1 :(得分:2)

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

(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函数已经执行此操作:

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