最近碰到的问题,有个数组,数组元素是对象,该对象的结构就如树的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
                }
            }
        }
    }
]

多叉树结构的数据,parent表示法转成children表示法的更多相关文章

  1. java科学计数法转换成普通计数法

    java科学计数法转换成普通计数法: String sjiachun = "12345E-10"; BigDecimal db = new BigDecimal(sjiachun) ...

  2. 多叉树结构:JSON数据解析(二)

    多叉树结构:JSON数据解析(二) 在上篇文章中提到了JSON数据解析的基本方法,但是方法效率太低,这里接着上篇文章写写如何利用多叉树结构,定义对象,实现JSON数据字段快速随机访问. JSON数据通 ...

  3. 多叉树结构:JSON数据解析(一)

    多叉树结构:JSON数据解析(一) 最近做一个实时数据搜索引擎的项目中,在项目架构的偏顶层需要写一个JSON数据解析的模块,由于目前JSON解析没有现成统一开源框架可以利用,目前只是手工利用com.a ...

  4. Bigtable:一个分布式的结构化数据存储系统

    Bigtable:一个分布式的结构化数据存储系统 摘要 Bigtable是一个管理结构化数据的分布式存储系统,它被设计用来处理海量数据:分布在数千台通用服务器上的PB级的数据.Google的很多项目将 ...

  5. [转] Protobuf高效结构化数据存储格式

    从公司的项目源码中看到了这个东西,觉得挺好用的,写篇博客做下小总结.下面的操作以C++为编程语言,protoc的版本为libprotoc 3.2.0. 一.Protobuf? 1. 是什么?  Goo ...

  6. wordpress数据库结构以及数据表之间的关系

    默认WordPress一共有以下11个表.这里加上了默认的表前缀 wp_ . wp_commentmeta:存储评论的元数据 wp_comments:存储评论 wp_links:存储友情链接(Blog ...

  7. Java实现树形结构的数据转Json格式

    在项目中难免会用到树形结构,毕竟这是一种常用的组织架构.楼主这里整理了两个实现的版本,可以直接拿来使用,非常方便. 楼主没有单独建项目,直接在以前的一个Demo上实现的.第一种,看下面代码: pack ...

  8. MySQL 5.7:非结构化数据存储的新选择

    本文转载自:http://www.innomysql.net/article/23959.html (只作转载, 不代表本站和博主同意文中观点或证实文中信息) 工作10余年,没有一个版本能像MySQL ...

  9. WordPress插件--WP BaiDu Submit结构化数据插件又快又全的向百度提交网页

    一.WP BaiDu Submit 简介 WP BaiDu Submit帮助具有百度站长平台链接提交权限的用户自动提交最新文章,以保证新链接可以及时被百度收录. 安装WP BaiDu Submit后, ...

随机推荐

  1. vim快速操作

    简明 VIM 练级攻略 vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类,你一定会对这个编辑器失去兴趣的.下面的文章翻译自<Learn ...

  2. Spring MVC新手教程(一)

    直接干货 model 考虑给用户展示什么.关注支撑业务的信息构成.构建成模型. control 调用业务逻辑产生合适的数据以及传递数据给视图用于呈献: view怎样对数据进行布局,以一种优美的方式展示 ...

  3. 10g异机恢复后EM无法启动故障处理一例

    之前在自己的測试环境上做了个异机恢复,原来的库上是配置过EM的,可是在恢复的库上去启动EM就报错了.以下看详细解决过程: PS:原主机名为zlm,恢复出来的主机名为bak [root@bak ~]# ...

  4. 摄像头ov2685中关于sensor id 设置的相关的寄存器地址【转】

    本文转载自:http://blog.csdn.net/morixinguan/article/details/51220992 OV2685 : CHIP_ID address : 0x300A   ...

  5. Android和H5交互-基础篇

    hybrid App开发也不是什么新鲜事了,其中native和h5之间的交互则是必不可少的.Android中是如何和H5交互的? 1.webView加载页面 我们都知道在Android中是通过webV ...

  6. Codeforces--621B--Wet Shark and Bishops(数学)

     B. Wet Shark and Bishops time limit per test 2 seconds memory limit per test 256 megabytes input ...

  7. sublime -text 删除已安装插件

    按ctr+shift +p然后输入remove 回车,再输入要删除的插件名

  8. 1tb等于多少g 1TB和500G有什么区别

    转自:http://www.a207.com/article/view_39392 移动硬盘.U盘是生活中常见的用品,他们的内存大小是什么标准.很多人对于1tb等于多少g和1tb和500g有什么区别不 ...

  9. 手写DAO框架(一)-从“1”开始

    背景: 很久(4年)之前写了一个DAO框架-zxdata(https://github.com/shuimutong/zxdata),这是我写的第一个框架.因为没有使用文档,我现在如果要用的话,得从头 ...

  10. Python生成器(yield)

    对于调用一个普通的Python函数,一般是从函数的第一行代码开始执行,结束于return语句.异常或者函数所有语句执行完毕.一旦函数将控制权交还给调用者,就意味着全部结束.函数中做的所有工作以及保存在 ...