深度数据结构匹配&先替换

时间:2015-06-05 21:07:25

标签: clojure functional-programming

我试图找出一种惯用,高效和/或功能强大的方法来执行以下操作:

我有一系列看起来像这样的地图:

({:_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或matchwalk等扩展中提供的工具(如果这些工具甚至是正确的库)。

此外,如果没有要求将其限制为特定策略(即仅在第一次匹配上进行修改,忽略其他匹配),感觉会更容易,但是那个' sa需求。理想情况下,解决方案可以适应不同的策略(例如,单个但随机定位的匹配)。策略的一个不变量是每个id / sub对只能使用一次。所以:

仅替换一个{1}} :related值的:idids的值匹配substitutes的相应值,其中一次出现是第一次(或第n次或第n次......)出现。

2 个答案:

答案 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)))
相关问题