如何按路径过滤地图内容

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

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

  1. 遍历整个地图并检查当前元素的完整路径是否在给定路径中。我不知道如何处理地图列表,以便将它们保存为地图列表。
  2. 从给定路径创建 select-keys 语句,但我又一次 运行 遇到地图列表问题 - 尤其是尝试解析具有某些共同深度的不同深度的路径。

我查看了 spectre,但没有看到任何可以做到这一点的东西。我想出的任何基于 mappostwalk 的解决方案在某些时候都会变得非常复杂。我一定是想错了。

如果有办法用原始 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}}}