Clojure:如何根据索引合并地图向量?

Clojure: How to merge vector of maps on the basis of index?

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

我尝试使用 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)))

代码片段使其更加清晰和精简。 非常感谢所有帮助我达到这一点的答案。

(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

一般情况下,你可以只使用mapmerge来合并hashmap的集合,但是当其中一个集合用完时它会结束合并。

您可以创建如下函数来 "extend" 集合具有相同的长度,然后像往常一样合并:

(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”集合,例如 (range)).

我会把它分解成多个简单的步骤,如下所示:

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

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