如何在JavaScript中将带有数组的深层JS对象动态减少为没有数组的JS对象?

时间:2018-12-21 16:10:40

标签: javascript arrays json object merge

我有一个对象数组,每个对象都有一个id作为键。每个对象代表ID的导航列表。

我需要将所有导航列表合并到一个大列表中,其中的叶子包含所属的ID。

结果对象应该不包含任何数组,但叶子将是ID的列表。

我已经尝试过递归解决方案,像合并和分配之类的lodash函数,但是我对对象的深度感到困惑

输入的JSON例如:

[
  {
    "id0": [
      {
        "Topitem 1": [
          {
            "Subitem 1": [
              "Leaf 1"
            ]
          }, {
            "Subitem 2": [
              "Leaf 2"
            ]
          }
        ]
      },
      {
        "Topitem 1": [
          {
            "Subitem 3": [
              "Leaf 1"
            ]
          }
        ]
      },
      {
        "Topitem 2": [
          "Leaf 1",
          "Leaf 3"
        ]
      },
      {
        "Topitem 3": [
          {
            "Subitem 1": [
              {
                "SubSubitem 1": [
                  "Leaf 4"
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "id1": [
      "Leaf 5"
    ]
  },
  {
    "id2": [
      "Leaf 5"
    ]
  },
  {
    "id3": [
      {
        "Topitem 1": [
          "Leaf 1",
          {
            "Subitem 2": [
              "Leaf 2",
              "Leaf 3"
            ]
          }
        ]
      }, {
        "Topitem 2": [
          "Leaf 1",
          "Leaf 2"
        ]
      }
    ]
  },
  {
    "id4": [
      "Leaf 5"
    ]
  }
]

预期输出为:

{
  "Topitem 1": {
    "Subitem 1": {
      "Leaf 1": ["id0"]
    },
    "Subitem 2": {
      "Leaf 2": ["id0","id3"],
      "Leaf 3": ["id3"]
    },
    "Subitem 3": {
      "Leaf 1": ["id0"]
    },
    "Leaf 1": ["id3"]
  },
  "Topitem 2": {
    "Leaf 1": ["id0","id3"],
    "Leaf 2": ["id0","id3"]
  },
  "Topitem 3": {
    "Subitem 1": {
      "SubSubitem 1": {
        "Leaf 4": ["id0"]
      }
    }
  },
  "Leaf5": ["id1","id2","id4"]
}

2 个答案:

答案 0 :(得分:1)

您可以采用一种迭代和递归的方法,即在第一次运行时使用一个不同的函数,即保存id以供以后在数组中收集,然后对嵌套的对象/键使用递归部分。 / p>

给定数据结构的主要问题是,最后,数组包含字符串而不是对象。

function convert(array) {

    function iter(array, object, value) {
        array.forEach(o => {
            if (!o || typeof o !== 'object') {
                (object[o] = object[o] || []).push(value);
                return;
            }
            Object
                .entries(o)
                .forEach(([k, v]) => iter(v, object[k] = object[k] || {}, value));
        });
    }

    var result = {};
    array.forEach(o => Object.entries(o).forEach(([k, v]) => iter(v, result, k)));
    return result;
}

var data = [{ id0: [{ "Topitem 1": [{ "Subitem 1": ["Leaf 1"] }, { "Subitem 2": ["Leaf 2"] }] }, { "Topitem 1": [{ "Subitem 3": ["Leaf 1"] }] }, { "Topitem 2": ["Leaf 1", "Leaf 3"] }, { "Topitem 3": [{ "Subitem 1": [{ "SubSubitem 1": ["Leaf 4"] }] }] }] }, { id1: ["Leaf 5"] }, { id2: ["Leaf 5"] }, { id3: [{ "Topitem 1": ["Leaf 1", { "Subitem 2": ["Leaf 2", "Leaf 3"] }] }, { "Topitem 2": ["Leaf 1", "Leaf 2"] }] }, { id4: ["Leaf 5"] }];

console.log(convert(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:0)

这是使用 lodashobject-scan 的解决方案。此解决方案无需递归

// const objectScan = require('object-scan');
// const lodash = require('lodash');

const myObject = [{ id0: [{ 'Topitem 1': [{ 'Subitem 1': ['Leaf 1'] }, { 'Subitem 2': ['Leaf 2'] }] }, { 'Topitem 1': [{ 'Subitem 3': ['Leaf 1'] }] }, { 'Topitem 2': ['Leaf 1', 'Leaf 3'] }, { 'Topitem 3': [{ 'Subitem 1': [{ 'SubSubitem 1': ['Leaf 4'] }] }] }] }, { id1: ['Leaf 5'] }, { id2: ['Leaf 5'] }, { id3: [{ 'Topitem 1': ['Leaf 1', { 'Subitem 2': ['Leaf 2', 'Leaf 3'] }] }, { 'Topitem 2': ['Leaf 1', 'Leaf 2'] }] }, { id4: ['Leaf 5'] }];

const convert = (obj) => objectScan(['**'], {
  filterFn: ({ key, value, context }) => {
    if (typeof value !== 'string') {
      return;
    }
    const k = [...key.slice(2).filter((e) => typeof e === 'string'), value];
    const cur = lodash.get(context, k);
    if (cur === undefined) {
      lodash.set(context, k, [key[1]]);
    } else {
      cur.push(key[1]);
    }
  }
})(obj, {});

console.log(convert(myObject));
// => { 'Leaf 5': [ 'id4', 'id2', 'id1' ], 'Topitem 2': { 'Leaf 2': [ 'id3' ], 'Leaf 1': [ 'id3', 'id0' ], 'Leaf 3': [ 'id0' ] }, 'Topitem 1': { 'Subitem 2': { 'Leaf 3': [ 'id3' ], 'Leaf 2': [ 'id3', 'id0' ] }, 'Leaf 1': [ 'id3' ], 'Subitem 3': { 'Leaf 1': [ 'id0' ] }, 'Subitem 1': { 'Leaf 1': [ 'id0' ] } }, 'Topitem 3': { 'Subitem 1': { 'SubSubitem 1': { 'Leaf 4': [ 'id0' ] } } } }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.7.1"></script>
<script src="https://bundle.run/lodash@4.17.20"></script>

免责声明:我是object-scan

的作者

这里有一个解决方案,我们可以跟踪堆栈,从而避免使用 lodash。不幸的是,它并不像我希望的那样优雅

// const objectScan = require('object-scan');

const myObject = [{ id0: [{ 'Topitem 1': [{ 'Subitem 1': ['Leaf 1'] }, { 'Subitem 2': ['Leaf 2'] }] }, { 'Topitem 1': [{ 'Subitem 3': ['Leaf 1'] }] }, { 'Topitem 2': ['Leaf 1', 'Leaf 3'] }, { 'Topitem 3': [{ 'Subitem 1': [{ 'SubSubitem 1': ['Leaf 4'] }] }] }] }, { id1: ['Leaf 5'] }, { id2: ['Leaf 5'] }, { id3: [{ 'Topitem 1': ['Leaf 1', { 'Subitem 2': ['Leaf 2', 'Leaf 3'] }] }, { 'Topitem 2': ['Leaf 1', 'Leaf 2'] }] }, { id4: ['Leaf 5'] }];

const convert = (obj) => objectScan(['[*]*.**[*]', '[*]*.**.*'], {
  breakFn: ({
    matchedBy, key, value, property, context
  }) => {
    if (matchedBy.includes('[*]*.**.*')) {
      const cur = context[context.length - 1];
      if (!(property in cur)) {
        cur[property] = {};
      }
      context.push(cur[property]);
    } else if (typeof value === 'string') {
      const cur = context[context.length - 1];
      if (!(value in cur)) {
        cur[value] = [];
      }
      cur[value].push(key[1]);
    }
  },
  filterFn: ({ matchedBy, context }) => {
    if (matchedBy.includes('[*]*.**.*')) {
      context.pop();
    }
  }
})(obj, [{}])[0];

console.log(convert(myObject));
// => { 'Leaf 5': [ 'id4', 'id2', 'id1' ], 'Topitem 2': { 'Leaf 2': [ 'id3' ], 'Leaf 1': [ 'id3', 'id0' ], 'Leaf 3': [ 'id0' ] }, 'Topitem 1': { 'Subitem 2': { 'Leaf 3': [ 'id3' ], 'Leaf 2': [ 'id3', 'id0' ] }, 'Leaf 1': [ 'id3' ], 'Subitem 3': { 'Leaf 1': [ 'id0' ] }, 'Subitem 1': { 'Leaf 1': [ 'id0' ] } }, 'Topitem 3': { 'Subitem 1': { 'SubSubitem 1': { 'Leaf 4': [ 'id0' ] } } } }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.7.1"></script>

免责声明:我是object-scan

的作者