Clojure:遍历映射向量并添加重复的元素

Clojure: traversing map vectors and adding elements that repeat

希望你们一切都好。好吧,我是 Clojure 编程的新手,需要一些帮助。我有以下地图矢量:

(def curves [{:curve_buyer "curve1" :curve_seller "curve2" :quantity 2 :value 3}
         {:curve_buyer "curve1" :curve_seller "curve3" :quantity 2 :value 4}
         {:curve_buyer "curve3" :curve_seller "curve2" :quantity 2 :value 3}
         {:curve_buyer "curve1" :curve_seller "curve3" :quantity 2 :value 3}
         {:curve_buyer "curve1" :curve_seller "curve2" :quantity 4 :value 4}
         {:curve_buyer "curve1" :curve_seller "curve3" :quantity 2 :value 4}])

我需要对该向量进行检查,它检查具有相同值的键(:curve_buyer 和 :curve_seller)。如果它们具有相同的值,我需要添加数量和值,否则 return 地图原样。拿上面的地图我会有以下 return.

(def curves [{:curve_buyer "curve1" :curve_seller "curve2" :quantity 6 :value 7}
             {:curve_buyer "curve2" :curve_seller "curve3" :quantity 2  :value 4}
             {:curve_buyer "curve3" :curve_seller "curve2" :quantity 2  :value 3}
             {:curve_buyer "curve1" :curve_seller "curva3" :quantity 4  :value 7}])

我需要一个函数来执行此操作。任何使用 clojure 函数的方法或可以解决我问题的函数的想法。

感谢您的关注,我愿意进行任何澄清。

您的输出可能是错误的,因为地图 {:curve_buyer "curve2" :curve_seller "curve3" :quantity 2 :value 4} 不在 curves.

这个任务可以用 group-by and merge-with 来解决。首先,按给定键将地图分组:

(def curves [{:curve_buyer "curve1" :curve_seller "curve2" :quantity 2 :value 3}
             {:curve_buyer "curve1" :curve_seller "curve3" :quantity 2 :value 4}
             {:curve_buyer "curve3" :curve_seller "curve2" :quantity 2 :value 3}
             {:curve_buyer "curve1" :curve_seller "curve3" :quantity 2 :value 3}
             {:curve_buyer "curve1" :curve_seller "curve2" :quantity 4 :value 4}
             {:curve_buyer "curve1" :curve_seller "curve3" :quantity 2 :value 4}])

(->> curves
     (group-by (juxt :curve_buyer :curve_seller)))
=>
{["curve1" "curve2"] [{:curve_buyer "curve1", :curve_seller "curve2", :quantity 2, :value 3}
                      {:curve_buyer "curve1", :curve_seller "curve2", :quantity 4, :value 4}],
 ["curve1" "curve3"] [{:curve_buyer "curve1", :curve_seller "curve3", :quantity 2, :value 4}
                      {:curve_buyer "curve1", :curve_seller "curve3", :quantity 2, :value 3}
                      {:curve_buyer "curve1", :curve_seller "curve3", :quantity 2, :value 4}],
 ["curve3" "curve2"] [{:curve_buyer "curve3", :curve_seller "curve2", :quantity 2, :value 3}]}

然后使用merge-with将每组中的地图合并为一个:

(->> curves
     (group-by (juxt :curve_buyer :curve_seller))
     (map (fn [[k v]] (apply merge-with 
                             (fn [o1 o2] (if (number? o1) (+ o1 o2) o1)) 
                             v))))
=>
({:curve_buyer "curve1", :curve_seller "curve2", :quantity 6, :value 7}
 {:curve_buyer "curve1", :curve_seller "curve3", :quantity 6, :value 11}
 {:curve_buyer "curve3", :curve_seller "curve2", :quantity 2, :value 3})

作为函数:

(defn summarize-by-keys [keys summary-fn list-of-maps]
  (->> list-of-maps
       (group-by (apply juxt keys))
       (map (fn [[k v]] (apply merge-with
                               summary-fn
                               v)))))

;; call it by:
(summarize-by-keys [:curve_buyer :curve_seller]
                   #(if (number? %1) (+ %1 %2) %1)
                   curves)

您可以使用reduce遍历curves的每个元素,一次创建或更新结果的相应元素。

(reduce (fn [res m]
          (let [{:keys [curve_buyer curve_seller quantity value]} m
                ;; idx is the index of the current buyer/seller
                ;; in the partial result or nil
                idx (first (keep-indexed (fn [i x]
                                           (when (and (= (:curve_buyer x) curve_buyer)
                                                      (= (:curve_seller x) curve_seller))
                                             i))
                                         res))]
            (if idx
              (update res
                      idx
                      #(assoc %
                              :quantity (+ quantity (:quantity %))
                              :value (+ value (:value %))))
              (conj res m))))
        []
        curves

最后的结果是:

[{:curve_buyer "curve1",
  :curve_seller "curve2",
  :quantity 6,
  :value 7}
 {:curve_buyer "curve1",
  :curve_seller "curve3",
  :quantity 6,
  :value 11}
 {:curve_buyer "curve3",
  :curve_seller "curve2",
  :quantity 2,
  :value 3}]

请注意,向量可以被视为关联集合,元素的索引是键。

(require '[net.cgrand.xforms :as x])

(->> curves
        (group-by (juxt :curve_buyer :curve_seller))
        (map last)
        (map (fn [xs] (into (first xs)
                          (x/multiplex
                              {:quantity (comp (map :quantity) (x/reduce +))
                               :value    (comp (map :value) (x/reduce +))}) xs))))

你也可以这样做:

首先想到的是把每一项都做成map的函数,包含index key和values来汇总:

(defn mappify [ks vs item]
  {(select-keys item ks) (select-keys item vs)})


(mappify [:curve_buyer :curve_seller]
         [:quantity :value]
         (first curves))
;;=>{{:curve_buyer "curve1", :curve_seller "curve2"}
;;   {:quantity 2, :value 3}}

然后你可以映射所有的项目,并合并它们:

(->> curves
     (map #(mappify [:curve_buyer :curve_seller]
                    [:quantity :value] %))
     (apply merge-with (partial merge-with +))
     (map (partial apply into)))

 ;;({:curve_buyer "curve1",
 ;; :curve_seller "curve2",
 ;; :quantity 6,
 ;; :value 7}
 ;;{:curve_buyer "curve1",
 ;; :curve_seller "curve3",
 ;; :quantity 6,
 ;; :value 11}
 ;;{:curve_buyer "curve3",
 ;; :curve_seller "curve2",
 ;; :quantity 2,
 ;; :value 3})

使用 reducemap。你可以先在reduce中做总结。 group-by看似简单,其实是用reduce进行分组,而不是汇总

(->> curves
     (reduce (fn [m {:keys [buyer seller q v]}]
               (update m [buyer seller]
                       #(-> %
                            (update :q (fnil + 0) q)
                            (update :v (fnil + 0) v))))
             {})
     (map (fn [[[buyer seller] m]]
            (assoc m :buyer buyer :seller seller))))

你也可以使用地图作为键

(->> curves
     (reduce (fn [m {:keys [q v] :as curve}]
               (update m (select-keys curve [:buyer :seller])
                       #(-> %
                            (update :q (fnil + 0) q)
                            (update :v (fnil + 0) v))))
             {})
     (map (fn [[km sm]] (merge km sm))))

所以作为函数

(defn summary-list
  [l key-keys val-keys]
  (->> (reduce (fn [acc m]
                 (update acc (select-keys m key-keys)
                         #(reduce (fn [vm vk]
                                    (update vm vk
                                            (fnil + 0)
                                            (get m vk)))
                                  % val-keys)))
               {} l)
       (map (partial reduce merge))))

像这样打电话

(summary-list curves [:curve_buyer :curve_seller] [:quantity :value])