项目中一个有关数据分类的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;

}

虽然结果是可行的,但是我觉得我的方式太麻烦了,不知道大家有没有更好的办法?我想到用递归,但是我不知道该怎么写这个递归。希望大家帮帮我。


4 回复

针对你的问题,我们可以使用递归来简化代码并提高可读性。递归是一种更简洁的方式来处理树形结构的数据。下面是一个使用递归的方法来将你的数据转换成所需的格式。

首先,我们需要定义一个函数,该函数接收一个数组和当前的 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));

解释

  1. groupByPid 函数:这个函数接收一个数据数组和一个 pid,然后返回所有 pid 匹配的数据。
  2. buildTree 函数:这是一个递归函数,它接收数据数组和一个 pid。它会找到所有 pid 匹配的数据,并为每个匹配的数据递归调用自身以构建子节点。
  3. 示例数据:这是你提供的示例数据。
  4. 调用 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属性来表示其子节点。这种方式不仅代码更加简洁,而且易于理解和维护。希望这对你有所帮助!

回到顶部