【问题标题】:Adding & removing elements in vector of maps in clojure在 clojure 中的地图向量中添加和删除元素
【发布时间】:2021-05-13 14:40:56
【问题描述】:

我有一个用例要在我的爱好项目中实现,我需要根据以下标准在地图、项目的向量中添加和删除元素

以下是我的代码中使用的输入数据结构

Items 是一个 LazySeq

[
{
:name “Bananas”
:line-no 0
:quantity 4
}
{
:name “Apple”
:line-no 2
:quantity 3
}
]

return-quantities 是一个 PersistentArrayMap,其中 line-no 作为键,如下所示

{
2 1
}

我的目标是获得一个合成向量,其中项目根据退货数量进行拆分

[
{
:name “Bananas”
:line-no 0
:quantity 4
}
{
:name “Apple”
:line-no 2
:quantity 1
:returned? true
}
{
:name “Apple”
:line-no 2
:quantity 2
}
]

下面是我实现该目标的示例代码

(let [items-to-be-added
      (for [[line-no return-quantity] return-quantities]
        (let [item (seek #(= line-no (:line-no %)) items)
              item-quantity (:quantity item)
              regular-item (when (pos? (- item-quantity return-quantity))
                             (assoc item :quantity (- item-quantity rma-quantity))
                             (assoc item :return-item? true))
              returned-item (assoc item :quantity rma-quantity)
              result [regular-item returned-item]]
          result))
      items-to-be-removed
      (for [[line-no] return-quantities]
        (let [item (seek #(= line-no (:line-no %)) items)]
          item))
      items-updated (filterv #(not (contains? (vec items-to-be-removed) %)) (vec items))]
  (doall (flatten (concat items-updated items-to-be-added))))

上述代码的问题是过滤(从向量中删除一组元素)不起作用。有人可以帮我解决矢量过滤的问题吗?也有人可以帮助我用更高效的代码来实现这一点,因为我觉得 impl 很复杂?

【问题讨论】:

  • 您能解释一下2 : 1 的含义吗?此外,您想要的输出也不清楚。还有,seek 是什么?
  • 它是一个键为 2 且值为 1 的映射?在我的问题的上下文中,这意味着第 2 行返回的数量是 1 @AlanThompson
  • 我已经使用 set 进行了过滤。有什么方法可以让代码更高效、更好,因为它看起来很复杂? @AlanThompson 换句话说,我可以用更少的代码更高效地实现它吗?
  • 您可能还对此文档来源列表感兴趣:github.com/io-tupelo/clj-template#documentation

标签: vector clojure updates


【解决方案1】:

在处理集合以为每个输入元素生成零个或多个输出元素时使用mapcat

(defn split-item [item quantity]
  (if quantity
    [(assoc item :returned? true :quantity quantity)
     (update item :quantity - quantity)]
    [item]))

(defn split-items [items quantities]
  (vec (mapcat #(split-item % (quantities (:line-no %))) items)))
(def items
  [{:name "Bananas", :line-no 0, :quantity 4}
   {:name "Apple",   :line-no 2, :quantity 3}])

(def return-quantities
  {2 1})

(split-items items return-quantities)
=> [{:name "Bananas", :line-no 0, :quantity 4}
    {:name "Apple",   :line-no 2, :quantity 1, :returned? true}
    {:name "Apple",   :line-no 2, :quantity 2}]

【讨论】:

  • 还有一个疑问,在拆分项功能中,我如何根据条件追加? (defn ^:private split-item [item return-qty] (If return-qty [(assoc item :return-line?true :quantity return-qty) (when (pos? (- (:quantity item) return-qty )) (update item :quantity - return-qty))] [item])) 上面的代码导致添加了空元素。然后我将不得不通过 (vec (remove nil? ()) 显式删除空元素。有没有更好的方法来避免添加空元素?
  • 要不添加任何元素,只需返回一个空集合(例如[])而不是nilmapcat对映射的结果进行concat,空集合的concat对结果没有影响。
  • 更新:在 REPL 实验之后,我发现返回 nil 是安全的,因为这也不会在结果中添加任何元素。
【解决方案2】:
(def items [{:name “Bananas”
             :line-no 0
             :quantity 4}
            {:name “Apple”
             :line-no 2
             :quantity 3}])

(def return-quantities
  {0 3
   2 1})
(defn add-return [items-by-line-no line-no return-quantity]
  (let [item (first (get items-by-line-no line-no))
        item-decreased (update item :quantity - return-quantity)
        item-returned (assoc item :quantity return-quantity :returned? true)]
    (update items-by-line-no line-no #(conj (assoc % 0 item-decreased) item-returned))))

(pprint (reduce-kv add-return (group-by :line-no items) return-quantities))
;; {0
;;  [{:name "Bananas", :line-no 0, :quantity 1}
;;   {:name "Bananas", :line-no 0, :quantity 3, :returned? true}],
;;  2
;;  [{:name "Apple", :line-no 2, :quantity 2}
;;   {:name "Apple", :line-no 2, :quantity 1, :returned? true}]}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2012-03-06
    • 2018-07-14
    • 2011-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多