项目中一个有关数据分类的Nodejs问题?
项目中一个有关数据分类的Nodejs问题?
今天遇到一个问题,以前都是使用oracle数据库这个问题直接在oracle中就解决了,但是现在是在mysql中没有办法使用迭代查询,所以就要自己写代码解决,我的问题是:
数据表中有这样一组种子数据:
id name pid
1 AA 0
2 BB 0
3 CC 0
4 DD 1
5 EE 1
6 FF 2
7 GG 3
8 H 3
9 II 3
10 JJ 2
....
数据量不大才几十条,
我是用nodejs开发的,所以从数据库读出来之后是一组对象数组,这样的
[obj1, obj2, obj3, obj4................]
这里的obj
里的形式是{id:1, name:'AA', pid:0, mark:'Y'}
(这里的mark字段是我自己加的,为了方便我后面的程序操作)。我要的结果就是把这个数组变成一个2维的数组,数组里面的每一项都是一个父类和他相应的子类(父类在第一个位置),这样的:
[[obj1, obj2, obj3], [obj4, obj5..],.... [obj11, obj12,...]]
其实如果在oracle数据库的话,直接在数据库方面就搞定了,但是mysql不行,除非要写存储过程太麻烦。
我的思路是,先从第一个的pid开始,依次查找和他相同pid的obj或者id和pid相同的obj,找到之后标记下,然后放到临时数组里面,当第一次遍历完成之后,把临时数组放到数组里面,然后在清除标记的项,然后剩下的再次进入循环。下面是我的实现方法:
function change(temp){
//存放结果的数组
var arr = new Array();
//外围循环控制数组数量
for(var i=0, x=0;i<temp.length;i++){
//临时数组存放每个单个数组项
var temparr = new Array();
//查找相同对象并且标记
for(var j=x+1;j<temp.length;j++){
//如果是父类就放到第一个位置
if(temp[x].pid == temp[j].id){
temparr.unshift(temp[j]);
//标记这个项是处理过的
temp[j].mark = 'D';
}
//如果是子类就放第一个的后面
if(temp[x].pid == temp[j].pid && temp[j].pid != 0){
temparr[temparr.length] = temp[j];
temp[j].mark = 'D';
}
//本身也要放进数组
if(j == temp.length-1){
temparr[temparr.length] = temp[x];
temp[x].mark = 'D';
}
}
//查找到的结果放到结果数组里
arr[arr.length] = temparr;
//清除标记的对象
for(var z=0, k=0;z<temp.length;z++){
//判断前一个是否已经删除
if(k==0){
z=0;
}
//如果是标记过的就删除
if(temp[z].mark == 'D'){
//这里没有多余,这个属性也用不着
delete temp[z].mark;
//清除标记过的项
temp.splice(z, 1);
//归零,方便下次从0开始
k=0;
//继续操作
continue;
}else{
//如果没有标记就累加,用于跳过
k++;
}
}
}
return arr;
}
虽然结果是可行的,但是我觉得我的方式太麻烦了,不知道大家有没有更好的办法?我想到用递归,但是我不知道该怎么写这个递归。希望大家帮帮我。
针对你的问题,我们可以使用递归来简化代码并提高可读性。递归是一种更简洁的方式来处理树形结构的数据。下面是一个使用递归的方法来将你的数据转换成所需的格式。
首先,我们需要定义一个函数,该函数接收一个数组和当前的 pid
,然后返回所有具有相同 pid
的对象组成的数组。
function groupByPid(data, pid = 0) {
const result = [];
data.forEach(item => {
if (item.pid === pid) {
result.push(item);
}
});
return result;
}
function buildTree(data, pid = 0) {
const children = groupByPid(data, pid);
children.forEach(child => {
child.children = buildTree(data, child.id);
});
return children;
}
// 示例数据
const data = [
{ id: 1, name: 'AA', pid: 0 },
{ id: 2, name: 'BB', pid: 0 },
{ id: 3, name: 'CC', pid: 0 },
{ id: 4, name: 'DD', pid: 1 },
{ id: 5, name: 'EE', pid: 1 },
{ id: 6, name: 'FF', pid: 2 },
{ id: 7, name: 'GG', pid: 3 },
{ id: 8, name: 'H', pid: 3 },
{ id: 9, name: 'II', pid: 3 },
{ id: 10, name: 'JJ', pid: 2 },
// 其他数据...
];
// 调用函数构建树
const tree = buildTree(data);
console.log(JSON.stringify(tree, null, 2));
解释
- groupByPid 函数:这个函数接收一个数据数组和一个
pid
,然后返回所有pid
匹配的数据。 - buildTree 函数:这是一个递归函数,它接收数据数组和一个
pid
。它会找到所有pid
匹配的数据,并为每个匹配的数据递归调用自身以构建子节点。 - 示例数据:这是你提供的示例数据。
- 调用 buildTree 函数:我们调用
buildTree
函数并将结果打印出来。
通过这种方式,你可以将原始数据转换成树形结构,每个节点都有一个 children
属性,包含其所有子节点。这样可以更容易地进行后续的操作,例如遍历、过滤等。
这个…感觉数据库不支持递归查询的话,服务端做好蛋疼啊。 要不重新设计一下数据结构,用空间换时间??
呵呵,你真逗!
你的想法是正确的,递归是一种更简洁、更优雅的方式来解决这类树形结构的问题。下面是一个使用递归来实现你需求的示例代码:
首先,你需要将数据转换成一个对象,以便于通过ID快速查找每个节点及其子节点。
const data = [
{ id: 1, name: 'AA', pid: 0, mark: 'N' },
{ id: 2, name: 'BB', pid: 0, mark: 'N' },
{ id: 3, name: 'CC', pid: 0, mark: 'N' },
{ id: 4, name: 'DD', pid: 1, mark: 'N' },
{ id: 5, name: 'EE', pid: 1, mark: 'N' },
{ id: 6, name: 'FF', pid: 2, mark: 'N' },
{ id: 7, name: 'GG', pid: 3, mark: 'N' },
{ id: 8, name: 'H', pid: 3, mark: 'N' },
{ id: 9, name: 'II', pid: 3, mark: 'N' },
{ id: 10, name: 'JJ', pid: 2, mark: 'N' }
];
// 将数据转换为对象
const nodesById = data.reduce((acc, node) => {
acc[node.id] = node;
return acc;
}, {});
// 使用递归函数构建树结构
function buildTree(parentId) {
const children = [];
for (const id in nodesById) {
const node = nodesById[id];
if (node.pid === parentId) {
children.push(node);
node.children = buildTree(node.id); // 递归调用
}
}
return children;
}
const tree = buildTree(0);
console.log(JSON.stringify(tree, null, 2));
上述代码将原始数据结构转换为树形结构,其中每个节点都有一个children
属性来表示其子节点。这种方式不仅代码更加简洁,而且易于理解和维护。希望这对你有所帮助!