Clojure:如何在索引的基础上合并地图向量?

时间:2019-08-20 04:04:41

标签: clojure clojurescript

我正在尝试合并地图矢量。

我尝试使用reduce方法执行此操作,但是无法检索预期的结果。

(def data '([{:padding-top "30px"} {:padding-top "40px"} {:padding-top "50px"}] [{:margin "40px"}]))

(reduce #(hash-map %1 %2) () data)

输入数据:

(def data '([{:padding-top "30px"} {:padding-top "40px"} {:padding-top "50px"}] [{:margin "40px"}]))

(defn merge-data
  [data]
)

预期输出:

(merge-data data)

({:padding-top "30px" :margin "40px"}
  {:padding-top "40px"}
  {:padding-top "50px"})

从JS的背景来看,我可以使用forEach和有条件的条件轻松构建预期的输出。但是如何以功能方式做到这一点?

解决方案:

我能够通过以下方式解决此问题

(defn merge-styles
  [& args]
  (let [max-count (apply max (map #(count %1) args))
        items (map #(take max-count (concat %1 (repeat nil))) args)]
    (apply map merge items)))

该代码段使其更清晰,更精简。 非常感谢所有帮助我起步的答案。

3 个答案:

答案 0 :(得分:1)

通常,您可以仅使用mapmerge来合并哈希图集合,但是当其中一个集合耗尽时,它将结束合并。

您可以创建如下所示的函数来“扩展”集合以使其具有相同的长度,然后照常进行合并:

(defn merge-all
  "Merges two sequences of maps using merge. Consumes all entries."
  [xs ys]
  (let [n  (max (count xs) (count ys))
        xs (take n (concat xs (repeat nil)))
        ys (take n (concat ys (repeat nil)))]
    (map merge xs ys)))

(def data [[{:padding-top "30px"} {:padding-top "40px"} {:padding-top "50px"}]
           [{:margin "40px"}]])

;; (apply merge-all data)
;; => ({:padding-top "30px", :margin "40px"} {:padding-top "40px"} {:padding-top "50px"})

请注意,在您的示例中,您在data周围使用了括号,但是在Clojure中,这意味着您希望像调用函数一样调用它。在上面的示例中,我将其切换为[]。另外,请注意,此功能取决于您实际上可以count传递给它的集合(在Clojure中,您可以拥有“ infinite ”集合,例如{{1} }。

答案 1 :(得分:0)

(map into [{:a 1} {:c 3}] (concat [{:b 2}] (repeat nil))) 产量 ({:a 1, :b 2} {:c 3})

map从两个向量中读取并将各自的索引应用于into,合并每个索引的映射。当其中一个集合用完时map停止时,我们需要将nil的值连接到较短的集合。

编辑:与往常一样,此问题之前已经得到解答:Using 'map' with different sized collections in clojure

答案 2 :(得分:0)

我会将其分解为多个简单的步骤,如下所示:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(def data [[{:padding-top "30px"} {:padding-top "40px"} {:padding-top "50px"}]
           [{:margin "40px"}]])

; be clear about the 2 sequences we are combining
(def data-1 (first data))
(def data-2 (second data))

(defn do-merge []
  (let [N (max (count data-1) (count data-2))]
    (vec (for [i (range N)]
           (let [map-1 (get data-1 i)
                 map-2 (get data-2 i)] ; returns `nil` if no value present
             (merge map-1 map-2) ; if merge a map & nil, it's a noop
             )))))

(dotest
  (is= (do-merge)
    [{:padding-top "30px", :margin "40px"}
     {:padding-top "40px"}
     {:padding-top "50px"}] ) )

您也可以使用nil填充较短的序列,然后使用(map merge ...),但是您必须确保首先获得正确的长度,而这些长度仍然涉及countmax ....不确定那是否更简单。