我正在尝试找出一种习惯的、高效的和/或功能强大的方法来做以下事情:
我有一个像这样的映射序列:
({:_id "abc" :related ({:id "123"} {:id "234"})}
{:_id "bcd" :related ({:id "345"} {:id "456"})}
{:_id "cde" :related ({:id "234"} {:id "345"})})
可以假设:id
域在任意一个:_id
中是唯一的。
另外,我有两组:
-
ids
like("234" "345")
和 -
substitutes
like({:id "111"} {:id "222"})
注意,在这个例子中,substitute只包含:id
并不意味着它可以被简化为一个id集合。这是一个问题的简化版本,实际数据在映射中有其他键/值对,这些键/值对必须出现。
我需要返回一个与原始序列相同的新序列,但substitutes
的值取代了ids
在所有项目的:related
集合中匹配id的第一个出现。那么最终的集合应该是这样的:
({:_id "abc" :related ({:id "123"} {:id "111"})}
{:_id "bcd" :related ({:id "222"} {:id "456"})}
{:_id "cde" :related ({:id "234"} {:id "345"})})
我确信我最终可以编写一些包含嵌套映射和条件的代码(以迭代的方式思考循环的循环),但对我来说,考虑到我可能拥有的工具,无论是在clojure中,我都没有考虑到功能或足够聪明。核心或扩展,如match
或walk
(如果它们甚至是正确的库来看)。
将:related
中:id
与ids
匹配的值替换为substitutes
中的对应值,且该值为第一次(或第n次或第n次…)出现
(def id-mapping (zipmap ids
(map :id substitutes)))
;; id-mapping -> {"345" "222", "234" "111"}
(clojure.walk/prewalk-replace id-mapping original)
假设集合名为results
:
(require '[clojure.zip :as z])
(defn modify-related
[results id sub]
(loop [loc (z/down (z/seq-zip results))
done? false]
(if (= done? true)
(z/root loc)
(let [change? (->> loc z/node :_id (= id))]
(recur (z/next (cond change?
(z/edit loc (fn [_] identity sub))
:else loc))
change?)))))
(defn modify-results
[results id sub]
(loop [loc (z/down (z/seq-zip results))
done? false]
(if (= done? true)
(z/root loc)
(let [related (->> loc z/node :related)
change? (->> related (map :_id) set (#(contains? % id)))]
(recur (z/next (cond change?
(z/edit loc #(assoc % :related (modify-related related id sub)))
:else loc))
change?)))))
(defn sub-for-first
[results ids substitutes]
(let [subs (zipmap ids substitutes)]
(reduce-kv modify-results results subs)))