【问题标题】:Splitting a vector into vector of vectors based on key根据键将向量拆分为向量的向量
【发布时间】:2016-03-08 19:09:34
【问题描述】:

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

[{: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,并用相同发票编号的所有条目填充它,然后一旦我们得到所有条目,将该向量插入 final-vector ,并返回该值。但相反,最终发票似乎总是一个空向量。我究竟做错了什么?这通常在 clojure 中是如何完成的?

【问题讨论】:

    标签: vector clojure functional-programming lisp


    【解决方案1】:

    让我们把它分成两个子问题:

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

    【讨论】:

    • 非常感谢。这比我想象的要容易得多。
    【解决方案2】:

    您可以使用group-bymapv 的组合(如果您想获得向量作为结果):

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

    【讨论】:

    • 我建议不要使用(mapv second),而是使用vals 函数从地图中提取所有值。
    • 好点。但是你需要另外用vec 包装它,以防你需要完全向量(而不是其他序列)
    【解决方案3】:

    你需要使用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)))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-09
      • 2020-06-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-18
      • 2020-12-23
      • 1970-01-01
      相关资源
      最近更新 更多