选择性地展平嵌套的JSON结构

2024-09-27 18:01:19 发布

您现在位置:Python中文网/ 问答频道 /正文

所以这是一个问题,我甚至不知道从哪里开始,所以即使是一个指向正确方向的指针也不错。在

所以我得到的数据是这样的:

data = {
   "agg": {
      "agg1": [
         {
            "keyWeWant": "*-20.0",
            "asdf": 0,
            "asdf": 20,
            "asdf": 14,
            "some_nested_agg": [
               {
                  "keyWeWant2": 20,
                  "to": 25,
                  "doc_count": 4,
                  "some_nested_agg2": {
                     "count": 7,
                     "min": 2,
                     "max": 5,
                     "keyWeWant3": 2.857142857142857,
                     "sum": 20
                  }
               },
               {
                  "keyWeWant2": 25,
                  "to": 30,
                  "doc_count": 10,
                  "some_nested_agg2": {
                     "count": 16,
                     "min": 2,
                     "max": 10,
                     "keyWeWant3": 6.375,
                     "sum": 102
                  }
               }
            ]
         },
         {
         ...
         },
         {
         ...
         },
         ...
      ]
   }
}

现在从这个例子来看,“agg”中有N个“agg1”结果,每个“agg1”结果中都有一个“keyWeWant”。每个“agg1”结果也有一个“some_nested_agg”结果列表,每个结果都包含一个“keyWeWant2”。每个“keyWeWant2”值都与层次结构中某个位置的单个“keyWeWant”值关联。类似地,每个“keyWeWant2”也包含“some_nested_agg2”的一组结果(这次不是一个列表,而是一个映射)。每个结果集都包含一个“keyWeWant3”。在

现在,我想在保持“keyWeWant”、“keyWeWant2”和“keyWeWant3”之间的关联的同时,将这个结构展平,得到如下结果:

我希望函数看起来像:

^{pr2}$

这是一个只有深度3的例子,但是可能有任意深度,有些嵌套值是列表,有些是数组/列表。在

我想做的是编写一个函数来接收我想要的键以及在哪里找到它们,然后获取这些键并反规范化。在

看起来像:

function_name(data_map, {
   "keyWeWant" : ['agg', 'agg1'],
   "keyWeWant2" : ['agg', 'agg1', 'some_nested_agg'],
   "keyWeWant" : ['agg', 'agg1', 'some_nested_agg', 'some_nested_agg2']
})

有什么想法吗?我熟悉Java、Clojure、javascript和Python,只是想找到一种相对简单的方法来解决这个问题。在


Tags: to列表datadoccountsomeminagg
2条回答

也许有更好的方法来解决这个特定的问题(使用一些ElasticSearch库或其他东西),但是这里有一个Clojure的解决方案,使用您请求的输入和输出数据格式。在

我把这个测试数据放在一个名为data.json的文件中:

{
    "agg": {
        "agg1": [
            {
                "keyWeWant": "*-20.0",
                "asdf": 0,
                "asdf": 20,
                "asdf": 14,
                "some_nested_agg": [
                    {
                        "keyWeWant2": 20,
                        "to": 25,
                        "doc_count": 4,
                        "some_nested_agg2": {
                            "count": 7,
                            "min": 2,
                            "max": 5,
                            "keyWeWant3": 2.857142857142857,
                            "sum": 20
                        }
                    },
                    {
                        "keyWeWant2": 25,
                        "to": 30,
                        "doc_count": 10,
                        "some_nested_agg2": {
                            "count": 16,
                            "min": 2,
                            "max": 10,
                            "keyWeWant3": 6.375,
                            "sum": 102
                        }
                    }]
            }]}
}

然后,Cheshire JSON library将数据解析为Clojure数据结构:

^{pr2}$

接下来,要获取的路径定义如下:

(def my-data-map
  {"keyWeWant"  ["agg", "agg1"],
   "keyWeWant2" ["agg", "agg1", "some_nested_agg"],
   "keyWeWant3" ["agg", "agg1", "some_nested_agg", "some_nested_agg2"]})

上面的data_map没有“:”,单引号改为双引号,最后一个“keyWeWant”改为“keyWeWant3”。在

下面的find-nested具有Clojure的^{}的语义,只有这样它才能在带有向量的映射上工作,并返回所有值而不是一个值。 当给find-nested一个搜索向量时,它会在一个嵌套映射中找到所有值,其中一些值可以由一个带有映射列表的向量组成。矢量中的每个贴图都会被检查。在

(defn find-nested
  "Finds all values in a coll consisting of maps and vectors.

  All values are returned in a tree structure:
  i.e, in your problem it returns (20 25) if you call it with
  (find-nested ['agg', 'agg1', 'some_nested_agg', 'keyWeWant2'] 
  my-data).

  Returns nil if not found."
  [ks c]
  (let [k (first ks)]
    (cond (nil? k)    c
          (map? c)    (find-nested (rest ks) (get c k))
          (vector? c) (if-let [e (-> c first (get k))]
                        (if (string? e) e ; do not map over chars in str
                            (map (partial find-nested (rest ks)) e))
                        (find-nested ks (into [] (rest c)))) ; create vec again
          :else       nil)))

find-nested查找搜索路径的值:

(find-nested ["agg", "agg1", "some_nested_agg", "keyWeWant2"] my-data) 
; => (20 25)

如果指向“keyWeWant”的所有路径都映射到my-data上,则这些是tree的切片:

(*-20.0
(20 25)
(2.857142857142857 6.375))

您所要求的结构(所有路径到达的最终结果)可以从function-name中的tree获得,如下所示:

(defn function-name
  "Transforms data d by finding (nested keys) via data-map m in d and 
  flattening the structure."
  [d m]
  (let [tree               (map #(find-nested (conj (second %) (first %)) d) m)
        leaves             (last tree)
        leaf-indices       (range (count leaves))
        results            (for [index leaf-indices]
                             (map (fn [slice]
                                    (if (string? slice)
                                      slice
                                      (loop [node (nth slice index)]
                                        (if node
                                          node
                                          (recur (nth slice (dec index)))))))
                                  tree))
        results-with-paths (mapv #(zipmap (keys m) %) results)
        json               (cheshire/encode results-with-paths)]
    json))

如果results大于特定的片,则results使用^{}后退一步。我认为对于更深层次的嵌套结构也会有效果——如果下一个切片的大小始终是前一个切片的两倍,或者应该是相同的大小,但我还没有对其进行测试。在

{{cd7>请求的{cd7>}将导致

[{
     "keyWeWant": "-20.0",
     "keyWeWant2": 20,
     "keyWeWant3": 2.857142857142857 }
 {
     "keyWeWant": "
-20.0",
     "keyWeWant2" 25,
     "keyWeWant3" 6.375 }]

/编辑 我知道你在寻找一个相对简单的解决方案,但事实并非如此。:-)可能有一个库中没有它。我很乐意知道如何简化它。在

这里有一个JavaScript(ES6)函数可以使用:

function flatten(data, keys) { var key = keys[0]; if (key in data) keys = keys.slice(1); var res = keys.length && Object.keys(data) .map( key => data[key] ) .filter( val => Object(val) === val ) .reduce( (res, val) => res.concat(flatten(val, keys)), []); return !(key in data) ? res : (res || [{}]).map ( obj => Object.assign(obj, { [key]: data[key] }) ); } // Sample data var data = { "agg": { "agg1": [ { "keyWeWant": "*-20.0", "asdf": 0, "asdf": 20, "asdf": 14, "some_nested_agg": [ { "keyWeWant2": 20, "to": 25, "doc_count": 4, "some_nested_agg2": { "count": 7, "min": 2, "max": 5, "keyWeWant3": 2.857142857142857, "sum": 20 } }, { "keyWeWant2": 25, "to": 30, "doc_count": 10, "some_nested_agg2": { "count": 16, "min": 2, "max": 10, "keyWeWant3": 6.375, "sum": 102 } } ] }, ] } }; // Flatten it by array of keys var res = flatten(data, ['keyWeWant', 'keyWeWant2', 'keyWeWant3']); // Output result console.log(res);

和13;
和13;

使用替代eh3路径>

如注释中所述,上面的代码不使用路径信息;它只查看所有数组。如果要查找的密钥也出现在应忽略的路径中,则可能会出现问题。在

以下替代方法将使用路径信息,该信息应作为子数组的数组传递,每个子数组首先列出路径键,最后一个元素是要保留的值键:

和13;

和13;

相关问题 更多 >

    热门问题