【问题标题】:How to build map for large json with unknown structure如何为具有未知结构的大型 json 构建地图
【发布时间】:2021-05-03 17:02:01
【问题描述】:

我有大量未知深度的 json 数据,我需要以以下结果格式构建地图。

const json = {
  1: {
    11: {
      111: [{ "111-0": "b" }, { "111-1": [{ "111-1-0": "vs" }] }],
      112: "asasd",
      ...
    },
    12: [{ "12-0": "sd" }],
    ...
  },
  2: [{ "2-0": "sd" }],
  ....
};

const result = {
  "1::11::111::A0::111-0": "b",
  "1::11::111::A1::111-1::A0::111-1-0": "vs",
  "1::11::112": "asasd",
  "1::12::A0::12-0": "sd",
  "2::A0::2-0": "sd",
};

我认为递归是解决这个问题的好方法,但我无法正确实现递归。

这是我目前的进度。这给出了不正确的输出。

const buildRecursion = (json, r, idx = 0, prev = "") => {
  Object.keys(json).forEach((key) => {
    prev += key + "::";
    if (Array.isArray(json[key])) {
      for (let [i, v] of json[key].entries()) {
        buildRecursion(v, r, i, prev);
      }
    } else if (typeof json[key] === "object") {
      buildRecursion(json[key], r, "", prev);
    } else {
      if (idx === "") {
        r[prev + "::" + key + "::"] = json[key];
      } else {
        r[prev + "::" + key + "::" + "::A" + idx] = json[key];
      }
    }
  });
};

【问题讨论】:

    标签: javascript json recursion


    【解决方案1】:

    (已更新以处理数组索引前面'A' 的缺失要求。)

    您可以在将更深的路径与所有叶节点的值相关联的函数之上构建它。我在其他答案中使用variants of this pathEntries function,但这个想法只是遍历对象,收集叶节点和通向它的路径。

    const pathEntries = (obj) =>
      Object (obj) === obj
        ? Object .entries (obj) .flatMap (
            ([k, x]) => pathEntries (x) .map (([p, v]) => [[Array.isArray(obj) ? Number(k) : k, ... p], v])
          ) 
        : [[[], obj]]
      
    
    const compress = (obj) => 
      Object .fromEntries (
        pathEntries (obj) 
          .map (([p, v]) => [p.map(n => Number(n) === n ? 'A' + n : n) .join ('::') , v])
      )
    
    const json = {1: {11: {111: [{ "111-0": "b" }, { "111-1": [{ "111-1-0": "vs" }] }], 112: "asasd", }, 12: [{ "12-0": "sd" }], }, 2: [{ "2-0": "sd" }]}
    
    console .log (compress (json))

    pathEntries 对您的数据的结果如下所示:

    
    [
      [["1", "11", "111", 0, "111-0"], "b"], 
      [["1", "11", "111", 1, "111-1", 0, "111-1-0"], "vs"], 
      [["1", "11", "112"], "asasd"], 
      [["1", "12", 0, "12-0"], "sd"], 
      [["2", 0, "2-0"], "sd"]
    ]
    

    然后compress 将这些路径数组映射为字符串,将'A' 添加到用于数组的数字路径的前面,并在这些结果上调用Object .fromEntries。如果您在没有Object .fromEntries 的环境中工作,那么填充很容易。

    虽然compress 专门针对此要求,但pathEntries 相当通用。

    【讨论】:

      【解决方案2】:

      我很高兴地说,您走在正确的轨道上。我所做的只是清理你的变量 (尤其是您对prev 的处理)并且效果很好。

      其他说明,

      • 字符串使用'' 而不是""
      • 考虑使用模板字符串(反引号)而不是 + 来连接字符串,因为这样做更简洁(通常情况下)。
      • 为了清楚起见,我将变量重命名为 json -> inputr -> outputprev -> key

      let input = {
          1: {
              11: {
                  111: [{"111-0": "b"}, {"111-1": [{"111-1-0": "vs"}]}],
                  112: "asasd",
              },
              12: [{"12-0": "sd"}],
          },
          2: [{"2-0": "sd"}],
      };
      
      let buildRecursion = (input, output = {}, key = []) => {
          if (Array.isArray(input))
              input.forEach((v, i) =>
                  buildRecursion(v, output, [...key, `A${i}`]));
          else if (typeof input === 'object')
              Object.entries(input).forEach(([k, v]) =>
                  buildRecursion(v, output, [...key, k]));
          else
              output[key.join('::')] = input;
          return output;
      };
      
      let result = buildRecursion(input);
      console.log(result);
      // {
      //  "1::11::111::A0::111-0": "b",
      //  "1::11::111::A1::111-1::A0::111-1-0": "vs",
      //  "1::11::112": "asasd",
      //  "1::12::A0::12-0": "sd",
      //  "2::A0::2-0": "sd",
      // }

      【讨论】:

      • 请注意,第一种情况 (if (Array.isArray(input)) ... buildRecursion(...)) 实际上是不必要的。第二种情况下的Object.entries 调用会做同样的事情。
      • 几乎,但是您不会在密钥中包含A。例如。结果中的最后一个条目将是 "2::0::2-0": "sd", 而不是 "2::A0::2-0": "sd"
      • 哦,我也完全错过了请求中的 A!
      【解决方案3】:

      如果值为数组,您可以使用reduce 方法和forEach。然后你可以使用Object.assign将递归调用结果分配给reduce的累加器,也就是结果对象。

      const json = {"1":{"11":{"111":[{"111-0":"b"},{"111-1":[{"111-1-0":"vs"}]}],"112":"asasd"},"12":[{"12-0":"sd"}]},"2":[{"2-0":"sd"}]}
      
      function f(data, prev = '') {
        return Object.entries(data).reduce((r, [k, v]) => {
          const key = prev + (prev ? '::' : '') + k
      
          if (typeof v === 'object') {
            if (Array.isArray(v)) {
              v.forEach((o, i) => Object.assign(r, f(o, key + `::A${i}`)))
            } else {
              Object.assign(r, f(v, key))
            }
          } else {
            r[key] = v
          }
      
          return r
        }, {})
      }
      
      const result = f(json);
      console.log(result)

      【讨论】:

        猜你喜欢
        • 2011-11-07
        • 2017-03-18
        • 1970-01-01
        • 1970-01-01
        • 2019-05-28
        • 1970-01-01
        • 2020-06-09
        • 2020-09-06
        • 1970-01-01
        相关资源
        最近更新 更多