我想选择深度嵌套映射的路径来保留。
例如:
{:a 1
:b {:c [{:d 1 :e 1}
{:d 2 :e 2}]
:f 1}
:g {:h {:i 4 :j [1 2 3]}}}
我想按路径选择,像这样:
(select-paths m [[:a]
[:b :c :e]
[:b :f]
[:g :h :i]])
返回
{:a 1
:b {:c [{:e 1}
{:e 2}]
:f 1}
:g {:h {:i 4}}}
本质上与Elasticsearch的fields
参数相同。路径参数的格式可以是其他的,这只是第一个想法。
我尝试了两种不同的解决方案
- 遍历整个地图,检查当前元素的完整路径是否在给定路径中。我不知道如何处理地图列表,使它们作为地图列表保存。
- 从给定的路径创建
select-keys
语句,但我再次遇到地图列表的问题-特别是试图解决具有一些共同深度的不同深度的路径。
我看了看spectre,但我没有看到任何东西会这样做。我想出的任何基于map
或postwalk
的解决方案在某些时候都会变得令人难以置信的复杂。我一定是想错了。
如果有办法做到这一点与原始json,那将是很好的。或者甚至是Java解决方案。
要实现你的目标没有简单的方法。[:b :c]
下序列的自动处理也存在问题。
你可以使用Tupelo Forest库实现其中的一部分。查看来自Clojure/Conj 2017的闪电谈话视频。
我在数据解构方面做了一些额外的工作,您可能会发现这些工作对构建tupelo.core/destruct
宏很有用(参见这里的示例)。您可以按照类似的大纲来构建针对特定问题的递归解决方案。
一个相关的项目是Meander。我已经开发了自己的版本,就像tupelo.core/destruct
的通用版本。给定这样的数据
(def skynet-widgets [{:basic-info {:producer-code "Cyberdyne"}
:widgets [{:widget-code "Model-101"
:widget-type-code "t800"}
{:widget-code "Model-102"
:widget-type-code "t800"}
{:widget-code "Model-201"
:widget-type-code "t1000"}]
:widget-types [{:widget-type-code "t800"
:description "Resistance Infiltrator"}
{:widget-type-code "t1000"
:description "Mimetic polyalloy"}]}
{:basic-info {:producer-code "ACME"}
:widgets [{:widget-code "Dynamite"
:widget-type-code "c40"}]
:widget-types [{:widget-type-code "c40"
:description "Boom!"}]}])
您可以使用如下模板搜索和提取数据:
(let [root-eid (td/add-entity-edn skynet-widgets)
results (td/match
[{:basic-info {:producer-code ?}
:widgets [{:widget-code ?
:widget-type-code wtc}]
:widget-types [{:widget-type-code wtc
:description ?}]}])]
(is= results
[{:description "Resistance Infiltrator" :widget-code "Model-101" :producer-code "Cyberdyne" :wtc "t800"}
{:description "Resistance Infiltrator" :widget-code "Model-102" :producer-code "Cyberdyne" :wtc "t800"}
{:description "Mimetic polyalloy" :widget-code "Model-201" :producer-code "Cyberdyne" :wtc "t1000"}
{:description "Boom!" :widget-code "Dynamite" :producer-code "ACME" :wtc "c40"}])))
这段代码是工作的(见这里),但它需要更多的润色。您可以将其用作构建通用select-paths
函数的指南。
你能详细说明这个问题是如何产生的或具体的背景吗?这可能会引出另一种解决方案。
解决此问题的一种方法是生成您接受的所有子路径的集合,然后编写递归函数,该函数遍历数据结构并跟踪到当前节点的路径。完成该操作的代码不需要很长:
(defn select-paths-from-set [current-path path-set data]
(cond
(map? data) (into {}
(remove nil?)
(for [[k v] data]
(let [p (conj current-path k)]
(if (contains? path-set p)
[k (select-paths-from-set p path-set v)]))))
(sequential? data) (mapv (partial select-paths-from-set current-path path-set) data)
:default data))
(defn select-paths [data paths]
(select-paths-from-set []
(into #{}
(mapcat #(take-while seq (iterate butlast %)))
paths)
data))
(select-paths {:a 1
:b {:c [{:d 1 :e 1}
{:d 2 :e 2}]
:f 1}
:g {:h {:i 4 :j [1 2 3]}}}
[[:a]
[:b :c :e]
[:b :f]
[:g :h :i]])
;; => {:a 1, :b {:c [{:e 1} {:e 2}], :f 1}, :g {:h {:i 4}}}