使用childIds从平面对象数组创建对象的嵌套数组

时间:2020-04-18 20:05:51

标签: javascript arrays json recursion data-structures

在(深层)嵌套的对象数组中,我有一个平面的对象数组。

我的平面数组(ID实际上是随机的,但为了清楚起见在此进行了更改,并且嵌套可能很深):

const tags = [
  {
    id: 'tag1',
    title: 'Tag 1',
    childIds: ['tag11', 'tag12'],
    root: true
  },
  {
    id: 'tag11',
    title: 'Tag 11',
    childIds: ['tag111', 'tag112'],
  },
  {
    id: 'tag12',
    title: 'Tag 12',
    childIds: ['tag121']
  },
  {
    id: 'tag111',
    title: 'Tag 111',
    childIds: []
  },
  {
    id: 'tag112',
    title: 'Tag 112',
    childIds: []
  },
  {
    id: 'tag121',
    title: 'Tag 121',
    childIds: []
  }
]

我想要的输出:

tagsNested = [
  {
    id: 'tag1',
    title: 'Tag 1',
    tags: [
      {
        id: 'tag11',
        title: 'tag 11',
        tags: [
          {
            id: 'tag111',
            title: 'Tag 111',
            tags: []
          },
          {
            id: 'tag112',
            title: 'Tag 112',
            tags: []
          }
        ]
      },
      {
        id: 'tag12',
        title: 'tag 12',
        tags: [
          {
            id: 'tag121',
            title: 'Tag 121',
            tags: []
          }
        ]
      }
    ]
  }

]

到目前为止,我一直努力将所有标签嵌套在任何标签下。

即我确实得到了一个嵌套数组,但是每个标签数组都包含所有标签

function unflatten(tag, nestedTags) {
  if (tag.childIds) {
    tag.childIds.forEach((childId) => {
      var childTag = tags.find((t) => t.id === childId)
      childTag.tags = unflatten(childTag, nestedTags)
      nestedTags.push(childTag)
    })
  }
  return nestedTags
}
const rootTag = tags.find((tag) => tag.root)
console.log(unflatten(rootTag, []))

我真的很难使用这些递归函数,并且弄清楚如何使return语句为我提供正确的数据。

4 个答案:

答案 0 :(得分:2)

您可以使用Map来为每个节点设置关键点,然后从中创建新的对象格式。最后,从地图中删除具有父项的条目,以便保留根。因此,实际上不需要root属性。它从给定的关系间接得出。此算法不使用该属性:

const tags = [{id: 'tag1',title: 'Tag 1',childIds: ['tag11', 'tag12'],root: true},{id: 'tag11',title: 'Tag 11',childIds: ['tag111', 'tag112'],},{id: 'tag12',title: 'Tag 12',childIds: ['tag121']},{id: 'tag111',title: 'Tag 111',childIds: []},{id: 'tag112',title: 'Tag 112',childIds: []},{id: 'tag121',title: 'Tag 121',childIds: []}];

let map = new Map(tags.map(({id, title, childIds}) => [id, { id, title, tags: [] }]));
tags.forEach(tag => map.get(tag.id).tags = tag.childIds.map(id => map.get(id)));
tags.forEach(tag => tag.childIds.forEach(id => map.delete(id)));
let tagsNested = [...map.values()];
console.log(tagsNested);

答案 1 :(得分:2)

这是一种递归方法。它是这样的:

  1. 给出root标签(或任何标签)和tagsArray(标签的扁平数组)
  2. 过滤root的所有子标签
  3. 然后为每个孩子找到其所有孩子标签
  4. 然后在没有子标签时返回标签

您可以尝试以下代码片段:

const tags = [
  {
    id: 'tag1',
    title: 'Tag 1',
    childIds: ['tag11', 'tag12'],
    root: true
  },
  {
    id: 'tag11',
    title: 'Tag 11',
    childIds: ['tag111', 'tag112'],
  },
  {
    id: 'tag12',
    title: 'Tag 12',
    childIds: ['tag121']
  },
  {
    id: 'tag111',
    title: 'Tag 111',
    childIds: []
  },
  {
    id: 'tag112',
    title: 'Tag 112',
    childIds: []
  },
  {
    id: 'tag121',
    title: 'Tag 121',
    childIds: []
  }
]

function buildTag({id, title, childIds}, tagsArray) {
  const tags = tagsArray
    .filter(tag => childIds.includes(tag.id))
    .map(tag => buildTag(tag, tagsArray))

  return {
      id,
      title,
      tags,
    }
}

const rootTag = tags.find((tag) => tag.root)
console.log([buildTag(rootTag, tags)])

/* 
tagsNested = [
  {
    id: 'tag1',
    title: 'Tag 1',
    tags: [
      {
        id: 'tag11',
        title: 'tag 11',
        tags: [
          {
            id: 'tag111',
            title: 'Tag 111',
            tags: []
          },
          {
            id: 'tag112',
            title: 'Tag 112',
            tags: []
          }
        ]
      },
      {
        id: 'tag12',
        title: 'tag 12',
        tags: [
          {
            id: 'tag121',
            title: 'Tag 121',
            tags: []
          }
        ]
      }
    ]
  }
]
*/

答案 2 :(得分:1)

您可以采用迭代方法,将对象作为对节点的引用。

const tags = [{ id: 'tag1', title: 'Tag 1', childIds: ['tag11', 'tag12'], root: true }, { id: 'tag11', title: 'Tag 11', childIds: ['tag111', 'tag112'] }, { id: 'tag12', title: 'Tag 12', childIds: ['tag121'] }, { id: 'tag111', title: 'Tag 111', childIds: [] }, { id: 'tag112', title: 'Tag 112', childIds: [] }, { id: 'tag121', title: 'Tag 121', childIds: [] }],
    tree = function (array) {
        var t = {},
            tree = [];

        array.forEach(({ id, title, childIds, root }) => {
            Object.assign(
                t[id] = t[id] || {},
                { id, title, tags: childIds.map(id => t[id] = t[id] || { id }) }
            );
            if (root) tree.push(t[id]);
        });

        return tree;
    }(tags);

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

答案 3 :(得分:0)

这是一个代码框-https://codesandbox.io/s/green-dawn-3gpvz?file=/src/index.js。与您的要求唯一的区别是结果只是对象而不是数组,但您可以将单个元素包装为数组

function resolveChildren(tag, allTags) {
  const { childIds, root, ...rest } = tag;
  if (!childIds) return rest;

  return {
    ...rest,
    tags: childIds.map(childId =>
      resolveChildren(allTags.find(t => t.id === childId), allTags)
    )
  };
}

resolveChildren(tags.find(tag => !!tag.root), tags)