将嵌套结果存储在clojure中的IF语句中

时间:2017-03-27 09:48:40

标签: clojure

尝试比较两个向量,并将差异存储在另一个向量中。

;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,我仍然是个新手:(。

3 个答案:

答案 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"}}

这不使用您最初使用的矢量,但显然矢量不是正确的数据结构,而关联的矢量更合适。