【问题标题】:Clojure sum maps with strings and numbers带有字符串和数字的 Clojure 求和映射
【发布时间】:2022-01-19 19:28:28
【问题描述】:

您好,我有 2 张这样的地图(可能有多个地图)

(def map1 {:a {:b 1} :c ["dog"]})
(def map2 {:a {:b 2} :c ["cat"]})

我需要将其作为返回 {:a {:b 3} :c ["dog" "cat"]}

我该怎么做?

【问题讨论】:

    标签: clojure


    【解决方案1】:

    这是一个想法。我没有对此进行测试,而且我很确定会出现更聪明的解决方案……尤其是现在我说这是一个答案(互联网上有人说某事错误的效果): )

    (reduce
      (fn [accum item]
        (let [b-accum (get-in accum [:a :b])
              b-item (get-in item [:a :b])
              new-b (+ b-accum b-item)
              new-c (vec (concat (:c accum) (:c item))]
          {:a {:b new-b} :c new-c}))
     [map1 map2])
      
    

    【讨论】:

      【解决方案2】:

      您可以使用merge-with 和自定义函数来解决合并地图时的冲突。

      (defn my-merge [a b]
        (merge-with (fn [a b]
                      (if (map? a)
                        (merge-with + a b)
                        (into a b)))
                    a b))
      

      或者递归定义:

      (defn my-merge [a b]
        (cond (vector? a) (into a b)
              (number? a) (+ a b)
              (map? a)    (merge-with my-merge a b)
              :else       (throw (ex-info "Unsupported values" {:values [a b]}))))
      

      【讨论】:

        【解决方案3】:

        使用泛型函数的解决方案

        您可以使用泛型函数——我们在这里称它为o+(运算符+)——通过利用Clojure 的多重分派能力。通过多次调度,您可以避免 if - elsecond 子句 - 用于区分参数类型/类 - 从而代码保持可扩展性(可以添加更多案例而无需更改现有代码)。 defmulti 定义末尾的函数是调度函数。在 defmethod 表单中 - 在实际函数参数列表(向量)之前,您列出了 Clojure 在决定使用哪种方法时查找的调度案例。

        (defmulti o+ (fn [& args] (mapv class args)))
        (defmethod o+ [Number Number] [x y] (+ x y)) ;; Numbers get added
        (defmethod o+ [clojure.lang.PersistentVector clojure.lang.PersistentVector]
          [x y] (into (empty x) (concat x y))) ;; Vectors concatentated
        (defmethod o+ [clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap] 
          [x y] (merge-with o+ x y)) ;; Maps merged-with #'o+ (recursive definition!)
        

        测试

        您可以将两个映射与(现在递归定义的)o+ 运算符融合:

        (def map1 {:a {:b 1} :c ["dog"]})
        (def map2 {:a {:b 2} :c ["cat"]})
        
        (o+ map1 map2) ;; or originally: (merge-with o+ map1 map2)
        ;; => {:a {:b 3}, :c ["dog" "cat"]}
        

        覆盖更深的嵌套地图

        由于此定义的递归特性 - 只要它们具有相同的结构,这也适用于更深层嵌套的映射 - 并且仅使用已定义的类案例(否则可以添加更多案例):

        (def mapA {:a {:b 2 :c {:d 1 :e ["a"] :f 3}} :g ["b"]})
        (def mapB {:a {:b 3 :c {:d 4 :e ["b" "e"] :f 5}} :g ["c"]})
        
        (o+ mapA mapB)
        ;;=> {:a {:b 5, :c {:d 5, :e ["a" "b" "e"], :f 8}}, :g ["b" "c"]}
        

        使用reduce 一次添加更多地图

        而且只要你可以在两个对象上应用o+,你就可以使用reduce处理任意数量的地图:

        (def mapA {:a {:b 2 :c {:d 1 :e ["a"] :f 3}} :g ["b"]})
        (def mapB {:a {:b 3 :c {:d 4 :e ["b" "e"] :f 5}} :g ["c"]})
        (def mapC {:a {:b 1 :c {:d 3 :e ["c"] :f 1}} :g ["a"]})
        
        (reduce o+ [mapA mapB mapC]) ;; this vector could contain much more maps!
        ;; => {:a {:b 6, :c {:d 8, :e ["a" "b" "e" "c"], :f 9}}, :g ["b" "c" "a"]}
        
        ;; or we could define in addition:
        (defmethod o+ :default [& args] (reduce o+ args))
        ;; which makes `o+` to a variadic function (function which can be
        ;; called with as many arguments you want)
        
        ;; then, whenever we add more than two arguments, it will be activated:
        (o+ mapA mapB mapC)
        ;; => {:a {:b 6, :c {:d 8, :e ["a" "b" "e" "c"], :f 9}}, :g ["b" "c" "a"]}
        
        ;; and this also works:
        (o+ 1 2 3 4 5 6)
        ;; => 21
        (o+ ["a"] ["b" "c"] ["d"])
        ;; => ["a" "b" "c" "d"]
        
        

        【讨论】:

          【解决方案4】:
          (require '[net.cgrand.xforms :as x])
          
          (let [map1 {:a {:b 1} :c ["dog"]}
                map2 {:a {:b 2} :c ["cat"]}]
                  (->> [map1 map2]
                      (into {}
                          (x/multiplex
                              {:a (comp (map :a) (map :b) (x/reduce +) (x/into [:b]) (x/into {}))
                               :c (comp (mapcat :c) (x/reduce conj))}))))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-11-15
            • 2011-01-16
            • 1970-01-01
            • 1970-01-01
            • 2018-10-18
            • 1970-01-01
            • 2015-03-17
            • 1970-01-01
            相关资源
            最近更新 更多