2

我正在努力解决以下问题......

给定一组地图

[
 {:a 1 :b 1 :c 1 :d 1}
 {:a 1 :b 2 :c 1 :d 2}
 {:a 1 :b 2 :c 2 :d 3}
 {:a 2 :b 1 :c 1 :d 5}
 {:a 2 :b 1 :c 1 :d 6}
 {:a 2 :b 1 :c 1 :d 7}
 {:a 2 :b 2 :c 1 :d 7}
 {:a 2 :b 3 :c 1 :d 7}
]

想减少/转换为...

{
 1 {:b [1 2] :c [1 2] :d [1 2 3]}
 2 {:b [1 2 3] :c 1 :d [5 6 7]}
}

group-by :a (主键)并为其他键累积不同的值。我可以以蛮力/命令的方式做到这一点,但很难弄清楚如何以 clojure 方式解决这个问题。

谢谢

4

4 回答 4

3

这是一个公认的不优雅的初稿解决方案:

(defn reducing-fn [list-of-maps grouping-key]
    (reduce (fn [m [k lst]]
              (assoc m k (dissoc (reduce (fn [m1 m2]
                                           (apply hash-map
                                                  (apply concat
                                                         (for [[k v] m2]
                                                           [k (conj (get m1 k #{}) v)]))))
                                         {}
                                         lst)
                                 grouping-key)))
            {}
            (group-by #(grouping-key %) list-of-maps)))

user> (reducing-fn [{:a 1 :b 1 :c 1 :d 1}
                    {:a 1 :b 2 :c 1 :d 2}
                    {:a 1 :b 2 :c 2 :d 3}
                    {:a 2 :b 1 :c 1 :d 5}
                    {:a 2 :b 1 :c 1 :d 6}
                    {:a 2 :b 1 :c 1 :d 7}
                    {:a 2 :b 2 :c 1 :d 7}
                    {:a 2 :b 3 :c 1 :d 7}] 
                   :a)
=> {2 {:c #{1}, :b #{1 2 3}, :d #{5 6 7}}, 1 {:c #{1 2}, :b #{1 2}, :d #{1 2 3}}}

明天会尝试找出一个更优雅的方法,现在就去睡觉:)

于 2012-05-29T22:43:14.017 回答
2
(use 'clojure.set)
(def data
  [
   {:a 1 :b 1 :c 1 :d 1}
   {:a 1 :b 2 :c 1 :d 2}
   {:a 1 :b 2 :c 2 :d 3}
   {:a 2 :b 1 :c 1 :d 5}
   {:a 2 :b 1 :c 1 :d 6}
   {:a 2 :b 1 :c 1 :d 7}
   {:a 2 :b 2 :c 1 :d 7}
   {:a 2 :b 3 :c 1 :d 7}
  ]
)

(defn key-join
  "join of map by key , value is distinct."
  [map-list]
  (let [keys (keys (first map-list))]
       (into {} (for [k keys] [k (vec (set (map #(% k) map-list)))]))))

(defn group-reduce [key map-list]
  (let [sdata (set map-list)
        group-value (project sdata [key])]
       (into {}
         (for [m group-value] [(key m) (key-join (map #(dissoc % key) (select #(= (key %) (key m)) sdata)))]))))
;;other version fast than group-reduce 
(defn gr [key map-list]
  (let [gdata (group-by key map-list)]
    (into {} (for [[k m] gdata][k (dissoc (key-join m) key)]))))
user=> (group-reduce :a data)
{1 {:c [1 2], :b [1 2], :d [1 2 3]}, 2 {:c [1], :b [1 2 3], :d [5 6 7]}}
user=> (gr :a data)
{1 {:c [1 2], :b [1 2], :d [1 2 3]}, 2 {:c [1], :b [1 2 3], :d [5 6 7]}}
于 2012-05-30T01:22:44.490 回答
2

另一种解决方案:

(defn pivot [new-key m]
  (apply merge 
    (for [[a v] (group-by new-key m)]
      {a (let [ks (set (flatten (map keys (map #(dissoc % new-key) v))))]
            (zipmap ks (for [k ks] (set (map k v)))))})))

ETA: new-key 将是这里的 :a 键,而 m 是您的输入映射。

第一个“for”解构了 group-by。这就是您通过输入“新键”对数据进行分区的地方。"for" 生成一个列表——它就像 Python 的列表推导。在这里,我们生成了一个映射列表,每个映射都有一个键,其值是一个映射。首先我们需要提取相关的密钥。这些键保存在“ks”绑定中。我们想要积累不同的价值。虽然我们可以使用 reduce 来做到这一点,因为关键字也是函数,我们可以使用它们在集合中提取,然后使用“set”来减少到不同的值。“zipmap”将我们的键及其相关值联系在一起。然后在主“for”之外,我们需要将此映射列表转换为单个映射,其键是“a”的不同值。

于 2012-05-30T14:25:17.860 回答
1

另一种解决方案:

(defn transform
  [key coll]
  (letfn [(merge-maps
            [coll]
            (apply merge-with (fnil conj #{}) {} coll))
          (process-key
            [[k v]]
            [k (dissoc (merge-maps v) key)])]
    (->> coll
      (group-by #(get % key))
      (map process-key)
      (into (empty coll)))))

不过,代码未经测试。

编辑:当然它不起作用,因为merge-with试图太聪明。

(defn transform
  [key coll]
  (letfn [(local-merge-with
            [f m & ms]
            (reduce (fn [m [k v]] (update-in m [k] f v))
                    m
                    (for [m ms e m] e)))
          (merge-maps
            [coll]
            (apply local-merge-with (fnil conj #{}) {} coll))
          (process-key
            [[k v]]
            [k (dissoc (merge-maps v) key)])]
    (->> coll
      (group-by #(get % key))
      (map process-key)
      (into (empty coll)))))
于 2012-05-30T06:02:34.793 回答