【问题标题】:Clojure: traversing map vectors and adding elements that repeatClojure:遍历地图向量并添加重复的元素
【发布时间】:2022-01-21 05:09:41
【问题描述】:

我希望你一切都好。好吧,我是 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)。如果它们具有相同的值,我需要添加数量和值,否则按原样返回地图。拿上面的地图我会得到以下回报。

(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 函数的任何方式或可以解决我的问题的函数的想法。

感谢您的关注,我愿意提供任何澄清。

【问题讨论】:

标签: clojure


【解决方案1】:

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

这个任务可以用group-by和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)

【讨论】:

    【解决方案2】:

    您可以使用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}]
    

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

    【讨论】:

      【解决方案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))))
      

      【讨论】:

        【解决方案4】:

        你也可以这样做:

        首先我们可以想出一个函数来制作每个项目的地图,包含索引键和值来总结:

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

        【讨论】:

          【解决方案5】:

          使用 reduce 和 map。你可以先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])
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多