递归处理对象数组的几种常见场景

1,983 阅读2分钟

1、递归求和。

数据类型举例:

const A = [  {    children: [      {        children: [          {            children: [              {                width: 20,              },              {                width: 30,              },              {                width: 20,              },            ],          },        ],      },    ],  },  {    width: 30,  },  {    children: [      {        width: 10,      },      {        width: 30,      },    ],  },]

求宽度之和

// 计算表格列宽总和
const sumColumn = arr => (arr.reduce((acc, cur) => (
Number(acc + (cur.children && cur.children.length ? sumColumn(cur.children) : cur.width))), 0)
);

sumColumn(A)结果为140;


2、拍平对象数组

数据举例:

同1例

拍平函数:

function flattenArrayss(arr) {
return arr.reduce((acc, current) => {
if (current.children && current.children.length) {
return [...acc, current, ...flattenArrayss(current.children)];
}
return [...acc, current];
}, []);
}

拍平结果:

flattenArrayss(A); 


3、根据末端节点找到所有父节点

比如找到focus为'5'的节点,及其所有父节点,用->相连接

数据举例:

同1例

解决函数:

const findTopParents = (array, label) => {
let stackTitle = [];
let going = true;

const walker = (a, b) => {
a.forEach((item) => {
if (!going) return;
stackTitle.push(item.focus);
if (item.focus === b) {
going = false;
} else if (item.children) {
walker(item.children, label);
} else {
stackTitle.pop();
}
});
if (going) {
stackTitle.pop();
}
};
walker(array, label);

const lgth = stackTitle.length - 1;
stackTitle = lgth ? `${stackTitle[lgth]}(${stackTitle.slice(0, lgth).join('->')})` : stackTitle.join('');

return stackTitle;
};

运行结果:

findTopParents(A, '5'); 

"5(1->2->3)"


4、根据末节点的值找到其所在对象其他元素的值

比如找到focus为'5'的节点,然后输出所在节点width的值

数据举例:


解决函数:

const findChildrenId = (id, list) => {
let allow = 1;
const walker = (a, b) => {
for (let i = 0; i < b.length; i++) {
if (b[i].focus === a) {
allow = b[i].width;
return;
} else if (b[i].children && b[i].children.length > 0) {
walker(a, b[i].children);
}
}
};
walker(id, list);
return allow;
};

运行结果:

findChildrenId('5',A); 

30


5、数组元素合并

数据举例

"responseParamList": [

{

"id": 1,
"parentId": 0,
"childrenNum": 4,
"paramName": "goods_search_response",
"paramType": "OBJECT",
"sourcePath": null,
"example": "",
"paramDesc": "response"
}, {
"id": 2,
"parentId": 1,
"childrenNum": 55,
"paramName": "goods_list",
"paramType": "OBJECT[]",
"sourcePath": null,
"example": "",
"paramDesc": "商品列表"
}, {
"id": 3,
"parentId": 2,
"childrenNum": 0,
"paramName": "activity_tags",
"paramType": "INTEGER[]",
"sourcePath": null,
"example": "",
"paramDesc": "商品活动标记数组"
}, {
"id": 4,
"parentId": 2,
"childrenNum": 0,
"paramName": "activity_tags",
"paramType": "INTEGER[]",
"sourcePath": null,
"example": "",
"paramDesc": "商品活动标记数组,例:[4,7]"
}, , {
"id": 5,
"parentId": 0,
"childrenNum": 0,
"paramName": "activity_tags",
"paramType": "INTEGER[]",
"sourcePath": null,
"example": "",
"paramDesc": "商品活动标记数组,例:[4,7],4-秒杀 7-ss等"
},

]

解决方法:

const mergeData = (list: any[]) => {
let data = list
data.forEach(ele => {
// 第一次forEach遍历到所有的数组对象,不做去重,数组依旧拥有6个元素
let parentId = ele.parentId
if (parentId === 0) {
//是根元素的hua ,不做任何操作,如果是正常的for-i循环,可以直接continue.
} else {
//如果ele是子元素的话 ,把ele扔到他的父亲的child数组中.它的父元素也可能是别人的子元素
//遍历6遍,数组依旧拥有6个元素,只不过有些有嵌套关系
data.forEach(d => {
if (d.id === parentId) {
let childArray = d.children
// 一则避免了去给d.child声明定义空数组
// childArray为undefined时,用push方法去报undefined.push的错
if (!childArray) {
childArray = []
}
// 用push保证同级别的对象有序追加
childArray.push(ele)
d.children = childArray
} //else的地方不做处理,保证了最深层的对象没有child属性
})
}
})
//去除重复元素
data = data.filter(ele => ele.parentId === 0)
return data
}