Skip to content

第 29 题:手写数组转树 #35

@lgwebdream

Description

@lgwebdream
Owner

欢迎在下方发表您的优质见解

Activity

changed the title [-]### 第 29 题:手写数组转树[/-] [+]第 29 题:手写数组转树[/+] on Jun 19, 2020
Genzhen

Genzhen commented on Jun 23, 2020

@Genzhen
Collaborator
let input = [
  {
    id: 1,
    val: "学校",
    parentId: null,
  },
  {
    id: 2,
    val: "班级1",
    parentId: 1,
  },
  {
    id: 3,
    val: "班级2",
    parentId: 1,
  },
  {
    id: 4,
    val: "学生1",
    parentId: 2,
  },
  {
    id: 5,
    val: "学生2",
    parentId: 3,
  },
  {
    id: 6,
    val: "学生3",
    parentId: 3,
  },
];
function buildTree(arr, parentId, childrenArray) {
  arr.forEach((item) => {
    if (item.parentId === parentId) {
      item.children = [];
      buildTree(arr, item.id, item.children);
      childrenArray.push(item);
    }
  });
}
function arrayToTree(input, parentId) {
  const array = [];
  buildTree(input, parentId, array);
  return array.length > 0 ? (array.length > 1 ? array : array[0]) : {};
}
const obj = arrayToTree(input, null);
console.log(obj);
Genzhen

Genzhen commented on Jun 23, 2020

@Genzhen
Collaborator

例如
[{id:1, parentId: 0}, {id:2, parentId:1},{id:3, parentId:1}]
把这个数组从顶级分类递归查找子分类,最终构建一个树状数组。结果输出如下
[{id:1, parentId: 0,children:[{id:2, parentId:1},{id:3, parentId:1}]}]
parentId为0 的是根节点

代码实现

// 输入
const tempArr = [{
    id: 1,
    parentId: 0
  },
  {
    id: 2,
    parentId: 1
  },
  {
    id: 3,
    parentId: 1
  },
  {
    id: 4,
    parentId: 2
  },
];

function arrayToTree(sourceArr) {
  sourceArr.forEach(item => {
    let parentId = item.parentId;
    if (parentId !== 0) {
      sourceArr.forEach(subitem => {
        if (subitem.id == parentId) {
          if (!subitem.children) {
            subitem.children = [];
          }
          subitem.children.push(item);
        }
      });
    }
  });
  return sourceArr.filter(item => item.parentId === 0);
}
console.log(arrayToTree(tempArr));
Genzhen

Genzhen commented on Jun 23, 2020

@Genzhen
Collaborator

数组转树的其中一种,排序数组转二叉搜索树

/**
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = null;
 *     this.right = null;
 * }
 */

var sortedArrayToBST = function (nums) {
    if (!nums.length) {
        return null
    };
    const root = new TreeNode(null);

    if (nums.length > 1) {
        root.left = sortedArrayToBST(nums.splice(0, nums.length / 2))
    };
    root.val = nums[0];
    root.right = sortedArrayToBST(nums.splice(1));
    return root;
};
523451928

523451928 commented on Jul 15, 2020

@523451928
var list = [
  { id: 1, name: '部门A', parentId: 0 },
  { id: 3, name: '部门C', parentId: 1 },
  { id: 4, name: '部门D', parentId: 1 },
  { id: 5, name: '部门E', parentId: 2 },
  { id: 6, name: '部门F', parentId: 3 },
  { id: 7, name: '部门G', parentId: 2 },
  { id: 8, name: '部门H', parentId: 4 }
];
function convert(list) {
  const map = list.reduce((acc, item) => {
    acc[item.id] = item
    return acc
  }, {})
  const result = []
  for (const key in map) {
    const item = map[key]
    if (item.parentId === 0) {
      result.push(item)
    } else {
      const parent = map[item.parentId]
      if (parent) {
        parent.children = parent.children || []
        parent.children.push(item)
      }
    }
  }
  return result
}
var result = convert(list)
fengmiaosen

fengmiaosen commented on Jul 20, 2020

@fengmiaosen
function convertStr(list) {
    const res = [];

    const map = new Map();

    for (let i = 0; i < list.length; i++) {
        map.set(list[i].id, list[i]);
    }

    for (let item of list) {
        if (item.parentId == null) {
            res.push(item);
        } else {
            const pItem = map.get(item.parentId);
            pItem.children = pItem.children || [];
            pItem.children.push(item);
        }
    }

    return res;
}
GolderBrother

GolderBrother commented on Jul 21, 2020

@GolderBrother
function arrayToTree(array = []) {
    // 获取祖先节点
    const root = array.shift();
    const tree = {
        id: root.id,
        val: root.val,
        children: toTree(root.id, array)
    };
    return tree;
}

function toTree(parentId, array = []) {
    const children = [];
    for (let index = 0, len = array.length; index < len; index++) {
        const node = array[index];
        // 找到儿子节点
        if (node.parentId === parentId) {
            children.push({
                id: node.id,
                val: node.val,
                children: toTree(node.id, array)
            });
        }

    }
    return children;
}
wang2498

wang2498 commented on Aug 3, 2020

@wang2498
// 非递归版
const arrayToTree = (arr = []) => {
  let map = {};
  let tree = [];
  for (let i in arr) {
    map[arr[i].id] = arr[i];
  }
  for (let i in map) {
    if (map[i].parentId) {
      if (!map[map[i].parentId].children) {
        map[map[i].parentId].children = [];
      }
      if (map[map[i].parentId].children) {
        map[map[i].parentId].children.push(map[map[i].id]);
      }
    } else {
      tree.push(map[i]);
    }
  }
  return tree;
}
xiaobei07

xiaobei07 commented on Aug 5, 2020

@xiaobei07

function arrToTre(params) {
const result = params.map(item => {
if (!item.children) {
item.children = []
}
const parent = findParent(item,params)
parent && parent.children.push(item)
return item
})
return result[lowIndex(params)]
}
function findParent(obj,arr) {
let result = null
arr.map(item => {
if (item.id === obj.parentId) {
item.children ? '' : item.children = []
result = item
}
})
return result
}

function lowIndex(arr) {
let ind = Infinity
let id = Infinity
arr.map((item,index) => {
if (item.id < id){
id = item.id
ind=index
}
})
return ind
}
const result = arrToTre(input)

v1nkon

v1nkon commented on Oct 19, 2020

@v1nkon
export const listToTree = (listData) => {
  let ids = [],
      objs = [];
  listData.map(item => {
      ids.push(item.id)
      objs.push({
          id:item.id,
          title: item.label,
          pid:item.pid,
          children: []
      })
  })
  let parents = []
  objs.map( cur => {  
      let index = ids.indexOf(cur.pid)
      if( index >=0 ){
          objs[index].children.push(cur)
      }else{
          parents.push({
              ...cur,
              isParent:true
          })
      }
  } )

  return parents
}

NameWjp

NameWjp commented on Oct 29, 2020

@NameWjp
export function listToTree(soure, pid = 0) {
  const result = []
  let temp
  
  soure.forEach((e) => {
    if (e.parentId === pid) {
      temp = listToTree(soure, e.id)
      if (temp.length > 0) {
         e.children = temp
      }
      result.push(e)
    }
  })
  return result 
}

8 remaining items

fanerge

fanerge commented on May 20, 2021

@fanerge

打印了一下各位大佬的代码,就想问一下,你们是怎么确定childnren一定是放在那个位置的?

什么意思呢?子节点的先后顺讯吗?可以看看比较好的解法

Luoyuda

Luoyuda commented on Jun 10, 2021

@Luoyuda
let input = [
    {
        id: 1,
        val: "学校",
        parentId: null,
    },
    {
        id: 2,
        val: "班级1",
        parentId: 1,
    },
    {
        id: 3,
        val: "班级2",
        parentId: 1,
    },
    {
        id: 4,
        val: "学生1",
        parentId: 2,
    },
    {
        id: 5,
        val: "学生2",
        parentId: 3,
    },
    {
        id: 6,
        val: "学生3",
        parentId: 3,
    },
    {
        id: 7,
        val: "学校2",
        parentId: null,
    },
    {
        id: 8,
        val: "班级1",
        parentId: 7,
    },
    {
        id: 9,
        val: "班级2",
        parentId: 7,
    },
    {
        id: 10,
        val: "学生1",
        parentId: 8,
    },
    {
        id: 11,
        val: "学生2",
        parentId: 9,
    },
    {
        id: 12,
        val: "学生3",
        parentId: 8,
    },
];
/**
 * 
 * @param {Array} arr 
 */
function listToTree(arr){
    let tree = []
    let temp = {}
    arr.forEach((item) => {
        item.children = []
        temp[item.id] = item
        if(!item.parentId){
            tree.push(item)
        }else{
            temp[item.parentId].children.push(item)
        }
    })
    return tree
}
console.log(listToTree(input))
/**
 * 
 * @param {Array} tree 
 */
function treeToList(tree){
    let list = []
    let temp = {}
    function dfs(children){
        for(let node of children){
            if(!temp[node.id]){
                list.push(node)
                temp[node.id] = true
            }
            dfs(node.children)
            delete node.children
        }
    }
    dfs(tree)
    return list
}
console.log(treeToList(JSON.parse(JSON.stringify(listToTree(input)))))
wjiantao

wjiantao commented on Jul 11, 2021

@wjiantao
function toTree(data) {
  var result = [];
  var map = {};
  data.forEach((item) => {
    map[item.id] = item;
  });
  data.forEach((item) => {
    var parent = map[item.parentId];
    if (parent) {
      (parent.children || (parent.children = [])).push(item);
    } else {
      result.push(item);
    }
  });
  return result;
}
console.log(toTree(input))
RaymoneLin21

RaymoneLin21 commented on Aug 12, 2021

@RaymoneLin21

function toTree(data) {
var result = [];
var map = {};
data.forEach((item) => {
map[item.id] = item;
});
data.forEach((item) => {
var parent = map[item.parentId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
result.push(item);
}
});
return result;
}
console.log(toTree(input))

guanghechen

guanghechen commented on Sep 5, 2021

@guanghechen

打印了一下各位大佬的代码,就想问一下,你们是怎么确定childnren一定是放在那个位置的?

@laihaoshan 先将数据转成树上的节点,然后根据 parentId 连边,parentIdnull 的那个就是根节点。

function toTree(data) {
  const idxMap = {}
  const nodes = []
  for (let i = 0; i < data.length; ++i) {
    const { id, val } = data[i]
    idxMap[id] = i
    nodes.push({ val, children: [] })
  }

  let root
  for (let i = 0; i < data.length; ++i) {
    const p = data[i].parentId
    if (p === null) root = nodes[i]
    else nodes[idxMap[p]].children.push(nodes[i])
  }
  return root
}
1uckyneo

1uckyneo commented on Sep 29, 2021

@1uckyneo
type Node<T> = {
  id: number;
  value: T;
  parentId: number;
};

type TreeNode<T> = {
  id: number;
  value: T;
  parentId: number;
  children: TreeNode<T>[];
};

const list: Node<string>[] = [
  { id: 1, value: 'V1', parentId: 0 },
  { id: 3, value: 'V2', parentId: 1 },
  { id: 4, value: 'V3', parentId: 1 },
  { id: 5, value: 'V4', parentId: 2 },
  { id: 6, value: 'V5', parentId: 3 },
  { id: 7, value: 'V6', parentId: 2 },
  { id: 8, value: 'V7', parentId: 4 },
];

const listToTree = <T>(list: Node<T>[]): TreeNode<T> | undefined => {
  const map = list.reduce<Map<number, TreeNode<T>>>((prev, curr) => {
    prev.set(curr.id, { ...curr, children: [] });
    return prev;
  }, new Map());


  let headId = 1;

  map.forEach((treeNode) => {
    const parent = map.get(treeNode.parentId);

    if (parent) {
      parent.children.push(treeNode);
    }

    if(treeNode.parentId === 0) {
      headId = treeNode.id;
    }
  });

  return map.get(headId);
};
safarishi

safarishi commented on Oct 21, 2021

@safarishi
var list = [
  { id: 1, name: '部门A', parentId: 0 },
  { id: 3, name: '部门C', parentId: 1 },
  { id: 4, name: '部门D', parentId: 1 },
  { id: 5, name: '部门E', parentId: 2 },
  { id: 6, name: '部门F', parentId: 3 },
  { id: 7, name: '部门G', parentId: 2 },
  { id: 8, name: '部门H', parentId: 4 },
  { id: 18, name: '部门嗨', parentId: 4 },
];

function list2tree(list) {
  let allIdList = list.map(item => item.id)

  function attachChildrenPropIfNeeded(item, _) {
    let children = list.filter(subItem => subItem.parentId === item.id).map(attachChildrenPropIfNeeded)

    return {
      ...item,
      ...children?.length && { children }
    }
  }

  return list.filter(item => !allIdList.includes(item.parentId)).map(attachChildrenPropIfNeeded)
}

console.log({ list, tree: list2tree(list) })
zizxzy

zizxzy commented on Nov 5, 2021

@zizxzy
var arr = [{ id: 1, pid: '-1' }, { id: 11, pid: '1' }, { id: 12, pid: '1' }]


const flatArrayToTree = (arr) => {
  let map = {};
  let tree = new Array();
  arr.map((value) => {
    map[value.id] = value;
    map[value.id].children = new Array();
  });

  arr.map((value) => {
    if (value.pid !== '-1') map[value.pid].children.push(value);
    else tree.push(value);
  })
  return tree;
}
console.log(flatArrayToTree(arr));
// 返回带有层级信息的树
const listToTreeWithLevel = function (list, parent, level) {
  let output = [];
  for (let node of list) {
    if (node.pid === parent) {
      node.level = level;
      let children = listToTreeWithLevel(list, node.id, level + 1);
      if (children.length) {
        node.children = children;
      }
      output.push(node);
    }
  }
  return output;
}

console.log(listToTreeWithLevel(arr, '-1', 0));
SnailOwO

SnailOwO commented on Dec 28, 2021

@SnailOwO

打印了一下各位大佬的代码,就想问一下,你们是怎么确定childnren一定是放在那个位置的?

因为js数组是引用的

zhou-pro

zhou-pro commented on Aug 24, 2022

@zhou-pro
//没看懂 还是写出来了
function Mtree(arr, pid = 0){
var tree = []
arr.forEach(item => {
  if(item.pid == pid){
    var children = Mtree(arr, item.id)
      if(children.length > 0){
        item.children = children
      }
        tree.push(item)
  }
})
return tree     
}
lang711

lang711 commented on Aug 27, 2022

@lang711
    function toTree(arr) {
      let [tree] = arr.map((item) => {
        item.children = [];
        item.children.push(
          ...arr.filter((child) => item.id === child.parentId)
        );
        return item;
      }).filter(item => item.parentId === null);
      return tree
    }
Kisthanny

Kisthanny commented on Mar 21, 2024

@Kisthanny
/**
 * 数组转树
 * 一般指将一个扁平的数组转化为树状结构
 * 其中每个对象已经具有独立id和指向父节点的parentId
 * 转化为一个根节点数组,其中每个对象增加children属性
 */

function arrayToTree(arr) {
  const rootNodes = [];
  const idMap = {};

  arr.map((node) => {
    node.children = [];
    idMap[node.id] = node;
  });

  arr.map((node) => {
    const { parentId } = node;
    if (parentId === null) {
      rootNodes.push(node);
    } else {
      idMap[parentId].children.push(node);
    }
  });

  return rootNodes;
}

const testArray = [
  {
    id: 1,
    parentId: null,
    data: 1,
  },
  {
    id: 2,
    parentId: 1,
    data: 2,
  },
  {
    id: 3,
    parentId: 2,
    data: 3,
  },
  {
    id: 4,
    parentId: null,
    data: 4,
  },
  {
    id: 5,
    parentId: 1,
    data: 5,
  },
];

console.dir(JSON.stringify(arrayToTree(testArray)));
[
    {
        "id": 1,
        "parentId": null,
        "data": 1,
        "children": [
            {
                "id": 2,
                "parentId": 1,
                "data": 2,
                "children": [
                    {
                        "id": 3,
                        "parentId": 2,
                        "data": 3,
                        "children": []
                    }
                ]
            },
            {
                "id": 5,
                "parentId": 1,
                "data": 5,
                "children": []
            }
        ]
    },
    {
        "id": 4,
        "parentId": null,
        "data": 4,
        "children": []
    }
]
HQHC

HQHC commented on May 7, 2024

@HQHC

function arrayToTree(arr, id = null) {
return arr
.filter(item => item.parentId === id)
.map(item => ({ ...item, children: arrayToTree(arr, item.id) }));
}

chamsonxie

chamsonxie commented on Feb 13, 2025

@chamsonxie

利用引用的特点 无递归 只遍历一边

let input = [
  {
    id: 2,
    val: "班级1",
    parentId: 1,
  },
  {
    id: 1,
    val: "学校",
    parentId: null,
  },
  {
    id: 4,
    val: "学生1",
    parentId: 2,
  },
  {
    id: 3,
    val: "班级2",
    parentId: 1,
  },
  {
    id: 5,
    val: "学生2",
    parentId: 3,
  },
  {
    id: 6,
    val: "学生3",
    parentId: 3,
  },
]

function buildTrees(arr, rootId) {
  const map = {} // 储存children的引用
  let root = {}
  arr.forEach((item) => {
    // 设置默认值
    map[item.id] = map[item.id] || []
    map[item.parentId] = map[item.parentId] || []

    item.children = map[item.id]
    map[item.parentId].push(item)
    if (item.parentId == rootId) {
      root = item
    }
  })
  return root
}

buildTrees(input, null)
coderlyu

coderlyu commented on Jun 15, 2025

@coderlyu
function arrayToTree(arr, parentId = null) {
  return arr
    .filter(item => item.parentId === parentId)
    .map(item => ({
      ...item,
      children: arrayToTree(arr, item.id)
    }));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @fengmiaosen@myzhoulang@lgwebdream@safarishi@Luoyuda

        Issue actions

          第 29 题:手写数组转树 · Issue #35 · lgwebdream/FE-Interview