如何按路径过滤映射内容



我想选择深度嵌套映射的路径来保留。

例如:

{: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参数相同。路径参数的格式可以是其他的,这只是第一个想法。

我尝试了两种不同的解决方案

  1. 遍历整个地图,检查当前元素的完整路径是否在给定路径中。我不知道如何处理地图列表,使它们作为地图列表保存。
  2. 从给定的路径创建select-keys语句,但我再次遇到地图列表的问题-特别是试图解决具有一些共同深度的不同深度的路径。

我看了看spectre,但我没有看到任何东西会这样做。我想出的任何基于mappostwalk的解决方案在某些时候都会变得令人难以置信的复杂。我一定是想错了。

如果有办法做到这一点与原始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}}}

相关内容

  • 没有找到相关文章

最新更新