简体   繁体   中英

How to turn a one dimensional array into a two dimensional one where the parents can go more than two levels deep?

I have a one dimensional array of objects and each object has an id and an id of its parent. In the initial array I have each element can have at most one child. So if the array looks like this:

{id: 3, parent: 5},
{id: 5, parent: null},
{id: 6, parent: 3},
{id: 1, parent: null},
{id: 4, parent: 7},
{id: 2, parent: 1},
{id: 7, parent: 2}

I need it to look similar to this:

{id: 5, parent: null, children: [
  {id: 3, parent: 5},
  {id: 6, parent: 3}
]},
{id: 1, parent: null, children: [
  {id: 2, parent: 1},
  {id: 7, parent: 2},
  {id: 4, parent: 7},
]}

I think the way I did it uses too much loops. Is there a better way?

 let items = [ {id: 3, parent: 5}, {id: 5, parent: null}, {id: 6, parent: 3}, {id: 1, parent: null}, {id: 4, parent: 7}, {id: 2, parent: 1}, {id: 7, parent: 2} ]; let itemsNew = []; items = items.map(function(x){ return {id: x.id, parent: x.parent, children: []}; }); // new array with parents only for(let i=items.length-1; i>=0; i--){ if(items[i].parent == null){ itemsNew.push(items[i]); items.splice(i, 1); } } for(let i=0; i<itemsNew.length; i++){ let childIndexes = findChildAll(itemsNew[i].id); // sort the indexes so splicing the array wont misplace them childIndexes.sort(function(a,b){ return ba; }); for(let j=0; j<childIndexes.length; j++){ itemsNew[i].children.push(items[childIndexes[j]]); items.splice(childIndexes[j], 1); } } // returns an array of indexes of all the element's children and their children function findChildAll(parentId){ for(let i=0; i<items.length; i++){ if(items[i].parent == parentId){ let resultArray = findChildAll(items[i].id); // is the result as an array add it to the index if(resultArray) return [i].concat(resultArray); // otherwise return just this index return [i]; } } } console.log(itemsNew); 

You could simplify it a lot by utilizing filter :

  for(const item of items) 
    item.children = items.filter(child => child.parent === item.id);

 const parents = items.filter(item => !item.parent);

Or if there are lots of nodes, might be benefitial for performance to use a Map:

 const itemsByID = new Map(items.map(item => [item.id, item]));

 const parents = [];

 for(const item of items) {
   if(item.parent) {
     const parent = itemsByID[item.parent];
     if(!parent.children) parent.children = [];
     parent.children.push(item);
  } else parents.push(item);
}

finding the children could be slow if the array gets big because the function searches always in the full array

const withParent = items.filter((item) => item.parent !== null);
const getDeepIdsWithId = (id) => {
    const result = [];
    while (true) {
        const i = withParent.find((item) => item.parent === id);
        if (!i) {
            break;
        }
        id = i.id;
        result.push(i);
    }
    return result;
};
const parent = items.filter((item) => item.parent === null).map((item) => ({...item, children: getDeepIdsWithId(item.id)}));

I think you can take a recursive approach some like this:

const array1 = [{id: 3, parent: 5},
    {id: 5, parent: null},
    {id: 6, parent: 3},
    {id: 1, parent: null},
    {id: 4, parent: 7},
    {id: 2, parent: 1},
    {id: 7, parent: 2}];

const getChild = (origi, fathers) => 
    origi.filter(
        originEle => originEle.parent && fathers.some(f => f.id === originEle.parent)
    )

const getChildren = (originArray, fathers) => {
    let childs = getChild(originArray, fathers);

    let continueFetching = childs && childs.length > 0;

    while (continueFetching) {
        const moreChilds = getChild(originArray, childs)
            .filter(m => !childs.some(c => c.id === m.id))

        if (
            moreChilds && 
            moreChilds.length > 0
        ) {
            childs = childs.concat(moreChilds);
            continueFetching = true;
        } else {
            continueFetching = false;
        }
    }

    return childs;
}

const result = array1
    .filter(
        a1 => !a1.parent
    )
    .map(
        aFather => ({
            id: aFather.id,
            parent: null,
            children: getChildren(array1, [aFather])
        })
    )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM