尝试比较两个向量,并将差异存储在另一个向量中。
;Data Set 1
{{:SKU "Apple" :QTY 10 :Status "In Stock" }
{:SKU "Banana" :QTY 10 :Status "In Stock" }
{:SKU "Mango" :QTY 0 :Status "Out of stock"}
{:SKU "XYZ" :QTY 10 :Status "In Stock" }
{:SKU "Grapes" :QTY 10 :Status "In Stock" }}
;Data Set 2
{{:SKU "Apple" :QTY 5 :Status "In Stock" }
{:SKU "Banana" :QTY 0 :Status "Out of Stock"}
{:SKU "Mango" :QTY 10 :Status "In Stock" }
{:SKU "XYZ" :QTY 10 :Status "In Stock" }
{:SKU "Pineapple" :QTY 10 :Status "In Stock" }}
我正在尝试获得类似
的输出{{:SKU "Apple" :Reason "Stock Change -5" }
{:SKU "Banana" :Reason "In Stock +10" }
{:SKU "Mango" :Reason "Out of stock -10" }
{:SKU "Grapes" :Reason "Missing" }
{:SKU "Pineapple" :Reason "Added" }}
我正在尝试使用嵌套的doseq构建逻辑,但我不知道如何将它写入clojure中的变量。
(defn compare_two_vectors
[data_set1 data_set2]
(doseq [recent_item data_set1]
(doseq [old_item data_set2]
(if (= (recent_item :SKU) (old_item :SKU))
(let [diffresults (clojure.data/diff recent_item old_item)
old_file (second diffresults)
new_file (first diffresults)
current_sku (recent_item :SKU)
]
;; How do I store results into a persistant variable?
)))))
然后我可以做
(println (compare_two_vectors data_set1 data_set2))
更新:或者让我知道什么是更好的选择。对于clojure,我仍然是个新手:(。
答案 0 :(得分:1)
事情是doseq
是副作用。在你的情况下,你不需要将任何东西放入一些可变变量中。相反,您可以映射集合并返回结果。其中一种方法是使用列表推导(for
):
(defn compare-data [data1 data2]
(for [recent-item data1
old-item data2
:when (= (:SKU recent-item) (:SKU old-item))
:let [[old-file new-file] (clojure.data/diff recent-item old-item)
current-sku (:SKU recent-item)]]
{:SKU current-sku :reason ...}))
(没有时间测试它,但仍然应该有效)
答案 1 :(得分:0)
顺序对你的载体有影响吗?如果没有(可能就是这种情况 - 您似乎使用SKU进行索引),最好先将它们转换为映射,以避免比较每个SKU的 O(n ^ 2)循环与每个。
(defn dataset->map [dataset]
(into {} (for [rec dataset] [(:SKU rec) (dissoc rec :SKU)])))
然后,您可以通过合并两个地图的键列表并应用clojure.core/distinct
来获取第一个数据集或第二个数据集或两者中的所有SKU的列表。
使用此功能,您可以遍历所有SKU以获得所需内容:
(defn compare-data [dataset-vec-1 dataset-vec-2]
(let [dataset-1 (dataset->map dataset-vec-1)
dataset-2 (dataset->map dataset-vec-2)
all-SKUs (distinct (concat (keys dataset-1) (keys dataset-2)))]
(for [sku all-SKUs
:let [rec-1 (get dataset-1 sku)
rec-2 (get dataset-2 sku)]]
{:SKU sku
:reason (cond
(nil? rec-1) "Added"
(nil? rec-2) "Missing"
:else ...)}))) ; whatever else you need to generate the reason
上面的代码应该以您想要的格式返回一个列表。
答案 2 :(得分:0)
您的数据存在两个问题。首先,矢量不是存储数据的好方法,这些数据不是位置的,而是相关的,你拥有的数据。其次,存储可以导出的冗余信息,因为存储数量为零的“缺货”,而存储数量大于零的“库存”。在具有许多项目的大型系统中,以这种方式缓存可导出数据是可以的,但在这种情况下,它只是不必要的冗余。因此,您最好以这种方式定义数据,IMO:
(def ds1 {"Apple" {:QTY 10}
"Banana" {:QTY 10}
"Mango" {:QTY 0}
"XYZ" {:QTY 10}
"Grapes" {:QTY 10}})
(def ds2 {"Apple" {:QTY 5}
"Banana" {:QTY 0}
"Mango" {:QTY 10}
"XYZ" {:QTY 10}
"Pineapple" {:QTY 10}})
此功能将根据您建议的数据结构进行所需的比较。首先,它执行差异以确定添加和删除的SKU。接下来,它对两者中存在的项执行diff,并使用合并函数来计算差异。
(defn sku-diff [sku-before sku-after]
(let [[removed added _] (d/diff (set (keys sku-before)) (set (keys sku-after)))
removed-map (apply hash-map (concat (interpose {:Status "Missing"} removed)
[{:Status "Missing"}]))
added-map (apply hash-map (concat (interpose {:Status "Added"} added)
[{:Status "Added"}]))
[before after _] (d/diff sku-before sku-after)
before (apply dissoc before removed)
after (apply dissoc after added)
merge-fn (fn [{before-qty :QTY} {after-qty :QTY}]
(let [stock-change (- after-qty before-qty)
text (cond (zero? before-qty) "In Stock +"
(zero? after-qty) "Out of stock "
:default "Stock Change ")]
{:Status (str text stock-change)}))
changed-map (merge-with merge-fn before after)]
(merge removed-map added-map changed-map)))
结果:
(sku-diff ds1 ds2)
=>
{"Pineapple" {:Status "Added"},
"Mango" {:Status "In Stock +10"},
"Grapes" {:Status "Missing"},
"Apple" {:Status "Stock Change -5"},
"Banana" {:Status "Out of stock -10"}}
这不使用您最初使用的矢量,但显然矢量不是正确的数据结构,而关联的矢量更合适。