我试图找出一种惯用,高效和/或功能强大的方法来执行以下操作:
我有一系列看起来像这样的地图:
({:_id "abc" :related ({:id "123"} {:id "234"})}
{:_id "bcd" :related ({:id "345"} {:id "456"})}
{:_id "cde" :related ({:id "234"} {:id "345"})})
可以假定:id
字段在任何一个:_id
内都是唯一的。
另外,我有两套:
ids
,例如("234" "345")
和substitutes
,例如({:id "111"} {:id "222"})
请注意,在此示例中仅替换为:id
的事实并不意味着它可以缩减为id的集合。这是问题的简化版本,真实数据在地图中有其他键/值对必须出现。
我需要返回一个与原始序列相同的新序列,但使用substitutes
中的值替换来自ids
的匹配ID的第一个出现所有项目的:related
个集合。那么最终的集合应该是什么样的:
({:_id "abc" :related ({:id "123"} {:id "111"})}
{:_id "bcd" :related ({:id "222"} {:id "456"})}
{:_id "cde" :related ({:id "234"} {:id "345"})})
我确信我最终可能会编写一些涉及嵌套映射和条件的内容(以循环循环的迭代术语思考),但这让我感觉就像我没有在功能上或巧妙地思考给定我可能在clojure.core或match
或walk
等扩展中提供的工具(如果这些工具甚至是正确的库)。
此外,如果没有要求将其限制为特定策略(即仅在第一次匹配上进行修改,忽略其他匹配),感觉会更容易,但是那个' sa需求。理想情况下,解决方案可以适应不同的策略(例如,单个但随机定位的匹配)。策略的一个不变量是每个id / sub对只能使用一次。所以:
仅替换一个{1}} :related
值的:id
与ids
的值匹配substitutes
的相应值,其中一次出现是第一次(或第n次或第n次......)出现。
答案 0 :(得分:0)
(def id-mapping (zipmap ids
(map :id substitutes)))
;; id-mapping -> {"345" "222", "234" "111"}
(clojure.walk/prewalk-replace id-mapping original)
答案 1 :(得分:0)
假设该集合被称为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)))