在javascript中从平面数组构建树数组
我有一个复杂的json文件,我必须用javascript来处理它,使其具有层次结构,以便以后构建树。 json的每个条目都有: id:唯一的id, parentId:父节点的id(如果节点是树的根,则为0) 级别:树中的深度级别 json数据已经“有序”。我的意思是一个条目在其上方有一个父节点或兄弟节点,在其下方有一个子节点或兄弟节点 输入:在javascript中从平面数组构建树数组,javascript,arrays,list,tree,Javascript,Arrays,List,Tree,我有一个复杂的json文件,我必须用javascript来处理它,使其具有层次结构,以便以后构建树。 json的每个条目都有: id:唯一的id, parentId:父节点的id(如果节点是树的根,则为0) 级别:树中的深度级别 json数据已经“有序”。我的意思是一个条目在其上方有一个父节点或兄弟节点,在其下方有一个子节点或兄弟节点 输入: { "People": [ { "id": "12", "parentId": "
{
"People": [
{
"id": "12",
"parentId": "0",
"text": "Man",
"level": "1",
"children": null
},
{
"id": "6",
"parentId": "12",
"text": "Boy",
"level": "2",
"children": null
},
{
"id": "7",
"parentId": "12",
"text": "Other",
"level": "2",
"children": null
},
{
"id": "9",
"parentId": "0",
"text": "Woman",
"level": "1",
"children": null
},
{
"id": "11",
"parentId": "9",
"text": "Girl",
"level": "2",
"children": null
}
],
"Animals": [
{
"id": "5",
"parentId": "0",
"text": "Dog",
"level": "1",
"children": null
},
{
"id": "8",
"parentId": "5",
"text": "Puppy",
"level": "2",
"children": null
},
{
"id": "10",
"parentId": "13",
"text": "Cat",
"level": "1",
"children": null
},
{
"id": "14",
"parentId": "13",
"text": "Kitten",
"level": "2",
"children": null
},
]
}
预期产出:
{
"People": [
{
"id": "12",
"parentId": "0",
"text": "Man",
"level": "1",
"children": [
{
"id": "6",
"parentId": "12",
"text": "Boy",
"level": "2",
"children": null
},
{
"id": "7",
"parentId": "12",
"text": "Other",
"level": "2",
"children": null
}
]
},
{
"id": "9",
"parentId": "0",
"text": "Woman",
"level": "1",
"children":
{
"id": "11",
"parentId": "9",
"text": "Girl",
"level": "2",
"children": null
}
}
],
"Animals": [
{
"id": "5",
"parentId": "0",
"text": "Dog",
"level": "1",
"children":
{
"id": "8",
"parentId": "5",
"text": "Puppy",
"level": "2",
"children": null
}
},
{
"id": "10",
"parentId": "13",
"text": "Cat",
"level": "1",
"children":
{
"id": "14",
"parentId": "13",
"text": "Kitten",
"level": "2",
"children": null
}
}
]
}
如果使用地图查找,则有一个有效的解决方案。如果父母总是比他们的孩子先来,你可以将两个for循环合并。它支持多个根。它在悬空分支上给出了一个错误,但可以修改为忽略它们。它不需要第三方库。据我所知,这是最快的解决方案
功能列表到目录树(列表){
var-map={},节点,根=[],i;
对于(i=0;i正如@Sander所提到的,假定一个预先排序的数组,但以下情况不适用。(不过,它假定您已经加载了underline.js,尽管它可以用普通javascript编写):
代码
//示例用法
var arr=[
{'id':1,'parentid':0},
{'id':2,'parentid':1},
{'id':3,'parentid':1},
{'id':4,'parentid':2},
{'id':5,'parentid':0},
{'id':6,'parentid':0},
{'id':7,'parentid':4}
];
unflatten=函数(数组、父级、树){
树=树的类型!=“未定义”?树:[];
父项=父项的类型!='undefined'?父项:{id:0};
var children=uz.filter(数组,函数(child){return child.parentid==parent.id;});
如果(!.isEmpty(儿童)){
if(parent.id==0){
树=儿童;
}否则{
父项['children']=子项
}
_.each(子,函数(子){unflatten(数组,子)});
}
回归树;
}
树=未整平(arr);
document.body.innerHTML=“”+(JSON.stringify(tree,null,”)
bower install list-to-tree --save
npm install list-to-tree --save
也有同样的问题,但我无法确定数据是否已排序。我不能使用第三方库,所以这只是普通的Js;输入数据可取自@Stephen的示例
var-arr=[
{'id':1,'parentid':0},
{'id':4,'parentid':2},
{'id':3,'parentid':1},
{'id':5,'parentid':0},
{'id':6,'parentid':0},
{'id':2,'parentid':1},
{'id':7,'parentid':4},
{'id':8,'parentid':1}
];
功能取消平台(arr){
var-tree=[],
mappedArr={},
阿瑞勒姆,
mappedElem;
//首先将数组的节点映射到对象->创建哈希表。
对于(变量i=0,len=arr.length;i
它可能是一个有用的包
bower install list-to-tree --save
npm install list-to-tree --save
安装:
var list = [
{
id: 1,
parent: 0
}, {
id: 2,
parent: 1
}, {
id: 3,
parent: 1
}, {
id: 4,
parent: 2
}, {
id: 5,
parent: 2
}, {
id: 6,
parent: 0
}, {
id: 7,
parent: 0
}, {
id: 8,
parent: 7
}, {
id: 9,
parent: 8
}, {
id: 10,
parent: 0
}
];
或
例如,请列出:
[{
"id": 1,
"parent": 0,
"child": [
{
"id": 2,
"parent": 1,
"child": [
{
"id": 4,
"parent": 2
}, {
"id": 5, "parent": 2
}
]
},
{
"id": 3,
"parent": 1
}
]
}, {
"id": 6,
"parent": 0
}, {
"id": 7,
"parent": 0,
"child": [
{
"id": 8,
"parent": 7,
"child": [
{
"id": 9,
"parent": 8
}
]
}
]
}, {
"id": 10,
"parent": 0
}];
使用包列表创建目录树:
function listToTree(data, options) {
options = options || {};
var ID_KEY = options.idKey || 'id';
var PARENT_KEY = options.parentKey || 'parent';
var CHILDREN_KEY = options.childrenKey || 'children';
var tree = [],
childrenOf = {};
var item, id, parentId;
for (var i = 0, length = data.length; i < length; i++) {
item = data[i];
id = item[ID_KEY];
parentId = item[PARENT_KEY] || 0;
// every item may have children
childrenOf[id] = childrenOf[id] || [];
// init its children
item[CHILDREN_KEY] = childrenOf[id];
if (parentId != 0) {
// init its parent's children object
childrenOf[parentId] = childrenOf[parentId] || [];
// push it into its parent's children object
childrenOf[parentId].push(item);
} else {
tree.push(item);
}
};
return tree;
}
结果:
import { isEmpty } from 'lodash'
export default function unflattenEntities(entities, parent = {id: null}, tree = []) {
let children = entities.filter( entity => entity.parent_id == parent.id)
if (!isEmpty( children )) {
if ( parent.id == null ) {
tree = children
} else {
parent['children'] = children
}
children.map( child => unflattenEntities( entities, child ) )
}
return tree
}
一个更简单的函数
npm安装列表到tree lite
listToTree(列表)
资料来源:
unflattenToObject = function(array, parent) {
var tree = {};
parent = typeof parent !== 'undefined' ? parent : {id: 0};
var childrenArray = array.filter(function(child) {
return child.parentid == parent.id;
});
if (childrenArray.length > 0) {
var childrenObject = {};
// Transform children into a hash/object keyed on token
childrenArray.forEach(function(child) {
childrenObject[child.id] = child;
});
if (parent.id == 0) {
tree = childrenObject;
} else {
parent['children'] = childrenObject;
}
childrenArray.forEach(function(child) {
unflattenToObject(array, child);
})
}
return tree;
};
var arr = [
{'id':1 ,'parentid': 0},
{'id':2 ,'parentid': 1},
{'id':3 ,'parentid': 1},
{'id':4 ,'parentid': 2},
{'id':5 ,'parentid': 0},
{'id':6 ,'parentid': 0},
{'id':7 ,'parentid': 4}
];
tree = unflattenToObject(arr);
函数列表树(数据、选项){
选项=选项| |{};
var ID|u KEY=options.idKey | |'ID';
var PARENT|u KEY=options.parentKey | |‘PARENT’;
var CHILDREN|u KEY=options.childrenKey||CHILDREN'CHILDREN';
var-tree=[],
childrenOf={};
var项、id、parentId;
对于(变量i=0,长度=data.length;i
以下是一个简单的助手函数,我根据上述答案创建了该函数,它是为巴别塔环境量身定制的:
const createDataTree = dataset => {
const hashTable = Object.create(null);
dataset.forEach(aData => hashTable[aData.ID] = {...aData, childNodes: []});
const dataTree = [];
dataset.forEach(aData => {
if(aData.parentID) hashTable[aData.parentID].childNodes.push(hashTable[aData.ID])
else dataTree.push(hashTable[aData.ID])
});
return dataTree;
};
也可以使用lodashjs(v4.x
function unflatten(records, rootCategoryId, parent, tree){
if(!_.isArray(tree)){
tree = [];
_.each(records, function(rec){
if(rec.parentId.indexOf(rootCategoryId)>=0){ // change this line to compare a root id
//if(rec.parentId == 0 || rec.parentId == null){ // example for 0 or null
var tmp = angular.copy(rec);
tmp.children = _.filter(records, function(r){
return r.parentId == tmp.id;
});
tree.push(tmp);
//console.log(tree);
_.each(tmp.children, function(child){
return unflatten(records, rootCategoryId, child, tree);
});
}
});
}
else{
if(parent){
parent.children = _.filter(records, function(r){
return r.parentId == parent.id;
});
_.each(parent.children, function(child){
return unflatten(records, rootCategoryId, child, tree);
});
}
}
return tree;
}
function listToTree(data, options) {
options = options || {};
var ID_KEY = options.idKey || 'id';
var PARENT_KEY = options.parentKey || 'parent';
var CHILDREN_KEY = options.childrenKey || 'children';
var item, id, parentId;
var map = {};
for(var i = 0; i < data.length; i++ ) { // make cache
if(data[i][ID_KEY]){
map[data[i][ID_KEY]] = data[i];
data[i][CHILDREN_KEY] = [];
}
}
for (var i = 0; i < data.length; i++) {
if(data[i][PARENT_KEY]) { // is a child
if(map[data[i][PARENT_KEY]]) // for dirty data
{
map[data[i][PARENT_KEY]][CHILDREN_KEY].push(data[i]); // add child to parent
data.splice( i, 1 ); // remove from root
i--; // iterator correction
} else {
data[i][PARENT_KEY] = 0; // clean dirty data
}
}
};
return data;
}
_(flatArray).forEach(f=>
{f.nodes=_(flatArray).filter(g=>g.parentId==f.id).value();});
var resultArray=_(flatArray).filter(f=>f.parentId==null).value();
var flatArray=
[{
id:1,parentId:null,text:"parent1",nodes:[]
}
,{
id:2,parentId:null,text:"parent2",nodes:[]
}
,
{
id:3,parentId:1,text:"childId3Parent1",nodes:[]
}
,
{
id:4,parentId:1,text:"childId4Parent1",nodes:[]
}
,
{
id:5,parentId:2,text:"childId5Parent2",nodes:[]
}
,
{
id:6,parentId:2,text:"childId6Parent2",nodes:[]
}
,
{
id:7,parentId:3,text:"childId7Parent3",nodes:[]
}
,
{
id:8,parentId:5,text:"childId8Parent5",nodes:[]
}];
function list2tree(data, opt) {
opt = opt || {};
var KEY_ID = opt.key_id || 'ID';
var KEY_PARENT = opt.key_parent || 'FatherID';
var KEY_CHILD = opt.key_child || 'children';
var EMPTY_CHILDREN = opt.empty_children;
var ROOT_ID = opt.root_id || 0;
var MAP = opt.map || {};
function getNode(id) {
var node = []
for (var i = 0; i < data.length; i++) {
if (data[i][KEY_PARENT] == id) {
for (var k in MAP) {
data[i][k] = data[i][MAP[k]];
}
if (getNode(data[i][KEY_ID]) !== undefined) {
data[i][KEY_CHILD] = getNode(data[i][KEY_ID]);
} else {
if (EMPTY_CHILDREN === null) {
data[i][KEY_CHILD] = null;
} else if (JSON.stringify(EMPTY_CHILDREN) === '[]') {
data[i][KEY_CHILD] = [];
}
}
node.push(data[i]);
}
}
if (node.length == 0) {
return;
} else {
return node;
}
}
return getNode(ROOT_ID)
}
var opt = {
"key_id": "ID", //节点的ID
"key_parent": "FatherID", //节点的父级ID
"key_child": "children", //子节点的名称
"empty_children": [], //子节点为空时,填充的值 //这个参数为空时,没有子元素的元素不带key_child属性;还可以为null或者[],同理
"root_id": 0, //根节点的父级ID
"map": { //在节点内映射一些值 //对象的键是节点的新属性; 对象的值是节点的老属性,会赋值给新属性
"value": "ID",
"label": "TypeName",
}
};
var arrayToTree = require('array-to-tree');
var dataOne = [
{
id: 1,
name: 'Portfolio',
parent_id: undefined
},
{
id: 2,
name: 'Web Development',
parent_id: 1
},
{
id: 3,
name: 'Recent Works',
parent_id: 2
},
{
id: 4,
name: 'About Me',
parent_id: undefined
}
];
arrayToTree(dataOne);
/*
* Output:
*
* Portfolio
* Web Development
* Recent Works
* About Me
*/
function getUnflatten(arr,parentid){
let output = []
for(const obj of arr){
if(obj.parentid == parentid)
let children = getUnflatten(arr,obj.id)
if(children.length){
obj.children = children
}
output.push(obj)
}
}
return output
}
// ListToTree.js
import _filter from 'lodash/filter';
import _map from 'lodash/map';
export default (arr, parentIdKey) => _map(_filter(arr, ar => !ar[parentIdKey]), ar => ({
...ar,
children: _filter(arr, { [parentIdKey]: ar.id }),
}));
// somewhere.js
import ListToTree from '../Transforms/ListToTree';
const arr = [
{
"id":"Bci6XhCLZKPXZMUztm1R",
"name":"Sith"
},
{
"id":"C3D71CMmASiR6FfDPlEy",
"name":"Luke",
"parentCategoryId":"ltatOlEkHdVPf49ACCMc"
},
{
"id":"aS8Ag1BQqxkO6iWBFnsf",
"name":"Obi Wan",
"parentCategoryId":"ltatOlEkHdVPf49ACCMc"
},
{
"id":"ltatOlEkHdVPf49ACCMc",
"name":"Jedi"
},
{
"id":"pw3CNdNhnbuxhPar6nOP",
"name":"Palpatine",
"parentCategoryId":"Bci6XhCLZKPXZMUztm1R"
}
];
const response = ListToTree(arr, 'parentCategoryId');
[
{
"id":"Bci6XhCLZKPXZMUztm1R",
"name":"Sith",
"children":[
{
"id":"pw3CNdNhnbuxhPar6nOP",
"name":"Palpatine",
"parentCategoryId":"Bci6XhCLZKPXZMUztm1R"
}
]
},
{
"id":"ltatOlEkHdVPf49ACCMc",
"name":"Jedi",
"children":[
{
"id":"C3D71CMmASiR6FfDPlEy",
"name":"Luke",
"parentCategoryId":"ltatOlEkHdVPf49ACCMc"
},
{
"id":"aS8Ag1BQqxkO6iWBFnsf",
"name":"Obi Wan",
"parentCategoryId":"ltatOlEkHdVPf49ACCMc"
}
]
}
]```
const flatData: any[] = Folder.ofCollection([
{id: '1', title: 'some title' },
{id: '2', title: 'some title', parentId: 1 },
{id: '3', title: 'some title', parentId: 7 },
{id: '4', title: 'some title', parentId: 1 },
{id: '5', title: 'some title', parentId: 2 },
{id: '6', title: 'some title', parentId: 5 },
{id: '7', title: 'some title', parentId: 5 },
]);
export default class Folder {
public static of(data: any): Folder {
return new Folder(data);
}
public static ofCollection(objects: any[] = []): Folder[] {
return objects.map((obj) => new Folder(obj));
}
public id: string;
public parentId: string | null;
public title: string;
public children: Folder[];
constructor(data: any = {}) {
this.id = data.id;
this.parentId = data.parentId || null;
this.title = data.title;
this.children = data.children || [];
}
}
public getTree(flatData: any[]): Folder[] {
const addChildren = (item: Folder) => {
item.children = tempChild[item.id] || [];
if (item.children.length) {
item.children.forEach((child: Folder) => {
addChildren(child);
});
}
};
const tempChild: any = {};
flatData.forEach((item: Folder) => {
const parentId = item.parentId || 0;
Array.isArray(tempChild[parentId]) ? tempChild[parentId].push(item) : (tempChild[parentId] = [item]);
});
const tree: Folder[] = tempChild[0];
tree.forEach((base: Folder) => {
addChildren(base);
});
return tree;
}
type ITreeItem<T> = T & {
children: ITreeItem<T>[],
};
type IItemKey = string | number;
function createTree<T>(
flatList: T[],
idKey: IItemKey,
parentKey: IItemKey,
): ITreeItem<T>[] {
const tree: ITreeItem<T>[] = [];
// hash table.
const mappedArr = {};
flatList.forEach(el => {
const elId: IItemKey = el[idKey];
mappedArr[elId] = el;
mappedArr[elId].children = [];
});
// also you can use Object.values(mappedArr).forEach(...
// but if you have element which was nested more than one time
// you should iterate flatList again:
flatList.forEach((elem: ITreeItem<T>) => {
const mappedElem = mappedArr[elem[idKey]];
if (elem[parentKey]) {
mappedArr[elem[parentKey]].children.push(elem);
} else {
tree.push(mappedElem);
}
});
return tree;
}
createTree(yourListData, 'id', 'parentId');
const people = [
{
id: "12",
parentId: "0",
text: "Man",
level: "1",
children: null
},
{
id: "6",
parentId: "12",
text: "Boy",
level: "2",
children: null
},
{
id: "7",
parentId: "12",
text: "Other",
level: "2",
children: null
},
{
id: "9",
parentId: "0",
text: "Woman",
level: "1",
children: null
},
{
id: "11",
parentId: "9",
text: "Girl",
level: "2",
children: null
}
];
function toTree(arr) {
let arrMap = new Map(arr.map(item => [item.id, item]));
let tree = [];
for (let i = 0; i < arr.length; i++) {
let item = arr[i];
if (item.parentId !== "0") {
let parentItem = arrMap.get(item.parentId);
if (parentItem) {
let { children } = parentItem;
if (children) {
parentItem.children.push(item);
} else {
parentItem.children = [item];
}
}
} else {
tree.push(item);
}
}
return tree;
}
let tree = toTree(people);
console.log(tree);
getTreeData = (items) => {
if (items && items.length > 0) {
const data = [];
const map = {};
items.map((item) => {
const id = item.id; // custom id selector !!!
if (!map.hasOwnProperty(id)) {
// in case of duplicates
map[id] = {
...item,
children: [],
};
}
});
for (const id in map) {
if (map.hasOwnProperty(id)) {
let mappedElem = [];
mappedElem = map[id];
/// parentId : use custom id selector for parent
if (
mappedElem.parentId &&
typeof map[mappedElem.parentId] !== "undefined"
) {
map[mappedElem.parentId].children.push(mappedElem);
} else {
data.push(mappedElem);
}
}
}
return data;
}
return [];
};
/// use like this :
const treeData = getTreeData(flatList);