js多叉树结构的数据,parent表示法转成children表示法

更新日期: 2018-11-12阅读量: 3025标签: 算法

最近碰到的问题,有个数组,数组元素是对象,该对象的结构就如树的parent表示法的节点一样。形象点讲就是该数组存放了树的所有“叶子节点”,并且叶子节点内存有父节点,一直到根节点为止,就如存了一条从叶子节点到根节点路径。

现在有要求是将这个数组转成一个children表示法的对象,即从根节点开始,每个节点存有其子节点数组。转化效果如下(节点必须有个唯一标识符,以下id就是,并且转化前后其他属性保持不变,这里为了显示简洁没有加入其他属性。):




核心思想是使用递归,新建唯一的根节点开始,不断生长出子节点。并再插入子节点时判断子节点是否存在,存在的话不插入,反之插入。注意所有将子节点插入到父节点children数组的操作,都必须保证被插入父节点已经是“新建的唯一根节点”下的,这样才能实现不断生长的效果。以下通过递归返回父节点的方式确保,返回前是一次插入操作,这时已经判断出“插入新节点”和“未插入新节点”,根据这两种情况,递归返回值就可以判断,如果插入新节点则返回该新节点作为父节点,反之返回已存在于“唯一根节点上的”的“该节点”作为父节点。  

var treeConverter = {
        result: null, //转化后的结果,是根节点,所有节点都是从根节点长出来的
        attributeName: 'id', //节点唯一标识符
        needFind: true, //是否查询节点在result中已经存在,为了优化效率
        transform: function (node) { //转化递归函数,参数:一个待插入节点
            if (node.parent != null) { //该节点有父节点
                var newNode = this.transform(node.parent); //递归进入,返回值为一个节点,用作父节点,该父节点必然存在于result中,这点由下面的算法可以控制
                if (this.needFind) {
                    for (var i = 0; i < newNode.children.length; i++) { //查找要插入的node子节点是否在newNode这个父节点中存在
                        if (newNode.children[i][this.attributeName] === node[this.attributeName]) {
                            return newNode.children[i]; //存在的话直接返回newNode父节点内的该子节点,该子节点必然存在于result中,作为返回值它将被用作上级递归的newNode,因此newNode必然存在于result中
                        }
                    }
                }
                this.needFind = false; //不存在的话,关闭之后递归的循环判断,因为待插入node节点不存在于result中,故而它的子节点一定不存在于result中,不用再循环判断
                delete node.parent; //删除该节点的parent属性,如果有的话
                node.children = []; //因为确定是要新插入的节点,没有children:[]属性,故给该节点增加children:[]属性
                newNode.children.push(node); //将该node节点push进newNode的子节点数组中
                return node; //return该新插入节点,作为递归返回值给上层,用作newNode父节点,node存在于result中故newNode存在于result中
            } else if (node.parent == null) { //该叶节点没有父节点,即为根节点
                delete node.parent; //删除该节点的parent属性,如果有的话
                if (this.result == null) { //根节点不存在
                    node.children = []; //给该节点增加children:[]属性
                    return this.result = node; //该节点赋给result,并return根节点,作为返回值它将被用作上级递归的newNode,因此newNode必然存在于result中
                } else {
                    return this.result // 直接return根节点,作为返回值它将被用作上级递归的newNode,因此newNode必然存在于result中
                }
            }
        },
        getSingle: function (node, attributeName) { //传入单个叶子节点,attributeName作为节点唯一标识符属性,返回单个转化结果
            this.result = null; //重置根节点
            this.needFind = true; //重置开启节点是否已存在判断
            this.attributeName = attributeName == null ? 'id' : attributeName; //唯一标识符默认为“id”
            this.transform(jsON.parse(jsON.stringify(node))); //复制出一个新的节点对象作为参数,保证不改变原有数据
            return this.result; //返回根节点
        },
        getWhole: function (nodes, attributeName) { //传入整个叶子节点数组,attributeName作为节点唯一标识符属性,返回整个转化结果
            this.result = null; //重置根节点
            this.attributeName = attributeName == null ? 'id' : attributeName; //唯一标识符默认为“id”
            nodes = JSON.parse(JSON.stringify(nodes)); //复制出一个新的节点对象作为参数,保证不改变原有数据
            nodes.forEach(item => { //循环调用转化方法
                this.needFind = true; //重置开启节点是否已存在判断,保证不插入重复节点
                this.transform(item);
            })
            return this.result; //返回根节点
        }
    }
    var result = treeConverter.getWhole(nodes); //调用


模拟数据: 

var nodes= [
    {
        id: 2,
        parent: {
            id: 5,
            parent: {
                id: 3,
                parent: null
            }
        }
    },
    {
        id: 1,
        parent: {
            id: 5,
            parent: {
                id: 3,
                parent: null
            }
        }
    },
    {
        id: 4,
        parent: {
            id: 7,
            parent: {
                id: 3,
                parent: null
            }
        }
    },
    {
        id: 14,
        parent: {
            id: 13,
            parent: {
                id: 9,
                parent: {
                    id: 8,
                    parent: {
                        id: 7,
                        parent: {
                            id: 3,
                            parent: null
                        }
                    }
                }
            }
        }
    },
    {
        id: 6,
        parent: {
            id: 7,
            parent: {
                id: 3,
                parent: null
            }
        }
    },
    {
        id: 10,
        parent: {
            id: 8,
            parent: {
                id: 7,
                parent: {
                    id: 3,
                    parent: null
                }
            }
        }
    }
]


原文来源:https://www.cnblogs.com/LQ996/archive/2018/11/11/9942545.html


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: https://www.fly63.com/article/detial/1323

JS数据结构与算法_树

一个树结构包含一系列存在父子关系的节点。每个节点都有一个父节点(除了顶部的第一个节点)以及零个或多个子节点:关于数的深度和高度的问题,不同的教材有不同的说法

JS实现链表_单链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域

为什么我认为数据结构与算法对前端开发很重要?

一个具有层级结构的数据,实现这个功能非常容易,因为这个结构和组件的结构是一致的,递归遍历就可以了。但是,由于后端通常采用的是关系型数据库,所以返回的数据通常会是这个样子:前端这边想要将数据转换一下其实也不难,因为要合并重复项

js生成32位uuid算法总汇_js 如何生成uuid?

GUID是一种由算法生成的二进制长度为128位的数字标识符。GUID 的格式为“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,其中的 x 是 0-9 或 a-f 范围内的一个32位十六进制数。在理想情况下,任何计算机和计算机集群都不会生成两个相同的GUID。

JavaScript十大排序必修算法

冒泡排序通过相邻元素的比较和交换,使得每一趟循环都能找到未有序数组的最大值或最小值,双向冒泡普通的冒泡排序在一趟循环中只能找出一个最大值或最小值,双向冒泡则是多一轮循环既找出最大值也找出最小

一些常用的语音特征提取算法

语言是一种复杂的自然习得的人类运动能力。成人的特点是通过大约100块肌肉的协调运动,每秒发出14种不同的声音。说话人识别是指软件或硬件接收语音信号,识别语音信号中出现的说话人,然后识别说话人的能力

Js实现首字母大写

一小段字母文本可以手动输入进行字母大小写的改变,如果是一大段文本只好借助程序来实现,中字母大小写转换是基本功能。 返回一个字符串,确保字符串的每个单词首字母都大写,其余部分小写

六种排序算法的Js实现

本文将介绍数据排序的基本算法和高级算法。这些算法都只依赖数组来存储数据。数组测试平台首先我们构造一个数组测试平台类,这些算法非常逼真地模拟了人类在现实生活中对数据的排序。

JS中常用的排序方法

通过相邻数据元素的交换,逐步将待排序序列变为有序序列,如果前面的数据大于后面的数据,就将两值进行交换,将数据进行从小到大的排序,这样对数组的第0个数据到N-1个数据进行一次遍历后

js字典对象_js实现字典Dictionary类操作

字典(Dictionary)是一种以 键-值对 形式存储数据的数据结构 ,其实对于javascript来说,字典类(Dictionary)的基础是Array类,js中的Array既是一个数组,同时也是一个字典。字典(Dictionary)类的基础是 Array 类。同之前的我们所看到的数据结构一样,字典类也应该有添加、删除、清空等操作。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!