根据键将向量拆分为向量的向量

Splitting a vector into vector of vectors based on key

我有一个对象向量,我想将其转换为对象向量向量,其中子向量中的每个对象对于特定成员具有相同的值。例如

[{:sku "105"}, {:sku "105"}, {:sku "120"}]

变成

[[{:sku "105"}, {:sku "105"}], [{:sku "120"}]]

我正在尝试掌握函数式思维方式,但我很确定我还没有掌握,因为我的代码看起来很笨拙。但这是我目前所拥有的:

(defn separate-by-invoice [original-invoices]
  (let [sorted-invoices (sort-by :invoice-number original-invoices)]
    (def temp-invoices [])
    (reduce (fn [final-invoices invoice]
      (let [prev-invoice-number (get-in (last temp-invoices) [:invoice-number])
            invoice-number (get-in invoice [:invoice-number])]
        (if (= prev-invoice-number invoice-number)
          (do
            (into temp-invoices invoice))
          (do
            (into final-invoices temp-invoices)
            (def temp-invoices [])
            (into temp-invoices invoice))))
        final-invoices)
      []
      sorted-invoices)))

基本上,我的想法是,我形成一个向量,temp-invoices,并用具有相同发票编号的所有条目填充它,然后一旦我们得到所有条目,将该向量插入最终-vector,并且 return 该值。但相反,final-invoices 似乎始终是一个空向量。我究竟做错了什么?这在 clojure 中通常是如何完成的?

让我们将其分解为两个子问题:

user> (def x [{:sku "105"}, {:sku "105"}, {:sku "120"}])
#<Var@18bc9d90: [{:sku "105"} {:sku "105"} {:sku "120"}]>

首先将相似的数据组合在一起:

user> (sort-by :sku x)
({:sku "105"} {:sku "105"} {:sku "120"})

然后在每次更改时拆分它:

user> (partition-by :sku (sort-by :sku x))
(({:sku "105"} {:sku "105"}) ({:sku "120"}))

也可以使用线程样式编写,使流程更易于阅读:

user> (->> x
           (sort-by :sku)
           (partition-by :sku)
           (mapv vec))
[[{:sku "105"} {:sku "105"}] [{:sku "120"}]]

您可以使用 group-by and mapv 的组合(以防您希望得到矢量结果):

(def data [{:sku "105"}, {:sku "105"}, {:sku "120"}])
(group-by :sku data) 
;; => {"105" [{:sku "105"} {:sku "105"}], "120" [{:sku "120"}]}

(mapv second (group-by :sku data))
;; => [[{:sku "105"} {:sku "105"}] [{:sku "120"}]]

可选地使用 threading 以获得更好的可读性:

(->> data
  (group-by :sku)
  (mapv second))

您需要使用group-by函数:

(ns tst.clj.core
  (:use clj.core 
        clojure.test 
        tupelo.core))

(def input    [ {:id 1 :sku 105} 
                {:id 2 :sku 105}
                {:id 3 :sku 120} ] )

(def result   [ [ {:id 1 :sku 105} 
                  {:id 2 :sku 105} ]
                [ {:id 3 :sku 120} ] ] )

(deftest t-separate-by-sku
  ; the result of 'group-by' is a map keyed by the grouping value 
  ; (the sku in this case)
  (is (= (group-by :sku input)
         { 105 [{:id 1, :sku 105} {:id 2, :sku 105}],  
           120 [{:id 3, :sku 120}] } ))
  ; we do not care about the grouping value, so just extract
  ; the values from the map with 'vals'
  (is (= (vals (group-by :sku input))   
         result)))