如何按路径过滤地图内容
How to filter map content by path
我想 select 保留深度嵌套地图的路径。
例如:
{:a 1
:b {:c [{:d 1 :e 1}
{:d 2 :e 2}]
:f 1}
:g {:h {:i 4 :j [1 2 3]}}}
我想 select 通过路径,像这样:
(select-paths m [[:a]
[:b :c :e]
[:b :f]
[:g :h :i]])
这会 return
{: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]
下的序列隐含的自动处理也是有问题的。
您可以使用 Clojure/Conj 2017 年的 Tupelo Forest library. See the Lightning Talk video 到达那里。
我在数据解构方面做了一些额外的工作,您可能会发现构建 tupelo.core/destruct
宏很有用(参见示例 here)。您可以按照类似的大纲为您的特定问题构建递归解决方案。
相关项目是 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"}])))
此代码有效(请参阅 here),但需要进一步完善。您可以将其用作构建通用 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}}}
我想 select 保留深度嵌套地图的路径。
例如:
{:a 1
:b {:c [{:d 1 :e 1}
{:d 2 :e 2}]
:f 1}
:g {:h {:i 4 :j [1 2 3]}}}
我想 select 通过路径,像这样:
(select-paths m [[:a]
[:b :c :e]
[:b :f]
[:g :h :i]])
这会 return
{: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]
下的序列隐含的自动处理也是有问题的。
您可以使用 Clojure/Conj 2017 年的 Tupelo Forest library. See the Lightning Talk video 到达那里。
我在数据解构方面做了一些额外的工作,您可能会发现构建 tupelo.core/destruct
宏很有用(参见示例 here)。您可以按照类似的大纲为您的特定问题构建递归解决方案。
相关项目是 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"}])))
此代码有效(请参阅 here),但需要进一步完善。您可以将其用作构建通用 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}}}