Clojure 如何减少这种收集?

Clojure 如何减少这种收集?,clojure,Clojure,我正在努力解决以下问题 给定一组地图 [ {: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]

我正在努力解决以下问题

给定一组地图

[
 {: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]}
}
分组依据:a(主键)并累积其他键的不同值。 我可以用蛮力/命令式的方式来解决这个问题,但我很难找到用clojure的方式解决这个问题的方法


谢谢

这里有一个公认不雅的初稿解决方案:

(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}}}
(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)))))
(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)))))})))
明天我将尝试想出一个更完善的方法,现在就上床睡觉:)

另一个解决方案:

(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}}}
(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)))))
(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)))))})))
但代码未经测试

编辑:当然不行,因为
合并试图太聪明

(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)))))
另一个解决方案:

(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}}}
(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)))))
(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:新键将是:a键,m是您的输入地图


第一个“for”按分解组。这就是通过输入“newkey”对数据进行分区的地方。“for”生成一个列表,就像Python的列表理解一样。在这里,我们将生成一个地图列表,每个地图都有一个键,其值是一个地图。首先,我们需要提取相关的密钥。这些钥匙固定在“ks”装订中。我们希望积累不同的值。虽然我们可以使用reduce来实现这一点,但因为关键字也是函数,所以我们可以使用它们在整个集合中进行提取,然后使用“set”来减少到不同的值。“zipmap”将我们的键及其关联值联系在一起。然后在主“for”之外,我们需要将这个映射列表转换为一个映射,其键是第一个映射条目的不同值“a”。

,我看到的是如何到达{:b[12]},而不是如何到达{:c[12]}。它看起来几乎应该是{:c[11]}。你能根据经验说明算法吗?因为:a是主键,前3个映射减少到1个记录,并且在3个映射内,当其他键的不同值被累加时,应导致:b[12]:c[12]和:d[1 2 3]。嵌套/递归组是否可以这样做?非常感谢。在真实数据上尝试了你的功能,效果很好。现在,我需要剖析你的函数并理解它。谢谢你的详细解释。喜欢你的解决方案的优雅和紧凑。谢谢你的解决方案。第二个有效。这是第一次看到letfn的使用