树型结构构建,模糊查询,过滤

news2024/11/15 17:15:19

一、前言

1、最近在做甘特图,有些需求和树型结构要求很大,看的是 pingCode,有搜索

在这里插入图片描述
2、还有抽取一部分树型结构的,如下是抽取上面的结构类型为需求的,重新组成树型

在这里插入图片描述

二、构建多颗树型结构

1、某些业务下,从数据库查询出来一堆数据后,希望构建树型结构,但是存在一种情况就是,可能这堆数据不是完整的,比如如下情况,我查询出来了除了D节点外的所有数据,那么这种情况下,如果使用正常的构建方式,那么构建出来的数据会丢失数据H,I,J,M,即使这四个节点的数据已经查询出来了,但是因为D节点缺失,导致无法链接上,如果是中间断开了,那断开的部分单独成一个树型结构
在这里插入图片描述

2、那有人说这不是正常的吗,你为啥D节点不查询出来,一颗完整的树是这样的啊,但是存在一些业务情况如下,上述的数据中除了D节点,其它节点的类型都是type1,而D节点的Type 是2,我现在就是想看type为1的,然后你给我形成树型结构

3、代码如下,其中模拟的时候,缺失节点999,所以结果如下,把节点999那一条结构,单独做一个树型结构返回,避免丢失
在这里插入图片描述

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;

import java.util.*;
import java.util.stream.Collectors;

class ChildHandle {

    public static void main(String[] args) {
        List<Node> mockData = mockData();
        List<List<Node>> lists = buildTree(mockData, 50);
        System.out.println(JSON.toJSONString(lists));
    }


    public static List<List<Node>> buildTree(List<Node> data, int maxDepth) {
        List<List<Node>> multipleTopNodeTreeResult = new ArrayList<>();
        if (CollUtil.isEmpty(data)) {
            return multipleTopNodeTreeResult;
        }
        Map<Integer, List<Node>> moduleMap = new HashMap<>(32);

        // 找出所有的父节点,因为有些数据并不是一个完整的树型树,如果是中间断开了,那断开的部分单独成一个树型结构
        HashSet<Integer> rootIds = new HashSet<>();
        Set<Integer> allIds = data.stream().map(Node::getId).collect(Collectors.toSet());
        for (Node module : data) {
            moduleMap.putIfAbsent(module.getPid(), new ArrayList<>());
            moduleMap.get(module.getPid()).add(module);

            // 当前的item的pid对应的数据不存在,说明从当前的item的pid就断开了,则为这个pid单独起一颗树
            if (!allIds.contains(module.getPid())) {
                rootIds.add(module.getPid());
            }
        }

        // 根据上述的判断,已经知道存在几颗树,则为每颗树构建结构
        rootIds.forEach(curTopNodeId -> {
            // 处理每一颗树
            List<Node> treeInCurTopNode = moduleMap.get(curTopNodeId);
            if (treeInCurTopNode != null) {
                // Sort root modules
                treeInCurTopNode.sort(Comparator.comparingInt(Node::getSerialNumber));
                for (Node rootModule : treeInCurTopNode) {
                    buildChildren(rootModule, moduleMap, 0, maxDepth);
                }
            } else {
                treeInCurTopNode = new ArrayList<>();
            }
            multipleTopNodeTreeResult.add(treeInCurTopNode);
        });

        return multipleTopNodeTreeResult;
    }

    private static void buildChildren(Node parentModule, Map<Integer, List<Node>> moduleMap, int depth, int maxDepth) {
        if (depth >= maxDepth) {
            // 达到深度限制,停止递归
            return;
        }

        List<Node> children = moduleMap.get(parentModule.getId());
        if (children != null) {
            // Sort children
            children.sort(Comparator.comparingInt(Node::getSerialNumber));
            parentModule.setChildren(children);
            for (Node child : children) {
                // 增加深度计数 限制最多递归多少次,避免OOM
                buildChildren(child, moduleMap, depth + 1, maxDepth);
            }
        }
    }


    /**
     * 模拟数据
     *
     * @return
     */
    private static List<Node> mockData() {
        List<Node> result = new ArrayList<>();
        result.add(new Node(1, 0, "Root1"));
        result.add(new Node(2, 1, "Root1 A"));
        result.add(new Node(3, 1, "Root1 B"));
        result.add(new Node(4, 2, "Root1 A.1"));
        result.add(new Node(5, 2, "Root1 A.2"));
        result.add(new Node(6, 3, "Root1 B.1"));
        result.add(new Node(7, 3, "Root1 B.2"));
        result.add(new Node(8, 3, "Root1 C"));
        result.add(new Node(9, 8, "Root1 D"));
        result.add(new Node(1000, 0, "Root2"));
        result.add(new Node(1001, 1000, "Root2 A"));
        result.add(new Node(1002, 1000, "Root2 B"));
        return result;
    }
}


class Node {
    private Integer id;

    /**
     * 父id,为0时说明自己就是第一层
     */
    private Integer pid;
    /**
     * 名称
     */
    private String name;
    /**
     * 排序
     */
    private int serialNumber;
    /**
     * 子集
     */
    private List<Node> children;


    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Node> getChildren() {
        return children;
    }

    public void setChildren(List<Node> children) {
        this.children = children;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public int getSerialNumber() {
        return serialNumber;
    }

    public void setSerialNumber(int serialNumber) {
        this.serialNumber = serialNumber;
    }

    public Node(Integer id, Integer pid, String name) {
        this.id = id;
        this.pid = pid;
        this.name = name;
    }

    public Node(Integer id, Integer pid, String name, int serialNumber, List<Node> children) {
        this.id = id;
        this.pid = pid;
        this.name = name;
        this.serialNumber = serialNumber;
        this.children = children;
    }
}

三、树型结构查询过滤

1、方法如下,其中存在一种情况就是查询到父节点满足过滤条件后,那么需不需要判断其子节点是否满足条件,如果不需要注释那段代码即可,如果需要接着往下判断则需要加上

/**
     * 树型查询
     *
     * @param tree          树型集合
     * @param key           搜索的字段名称
     * @param value         搜索的值
     * @param childNodeName 子节点名称
     * @param <T>           数据具体对象
     * @return tree
     */
    public <T> List<T> filterTree(List<T> tree, Function<JSONObject, Boolean> filterCondition, String childNodeName) {
        // 这个方法的原始文章 https://blog.csdn.net/weixin_44748212/article/details/131692471
        // 如果要保留子节点的话把注释的(// 去除子节点start - end )这段代码删掉即可
        if (CollUtil.isEmpty(tree)) {
            return new ArrayList<>();
        }
//        JSONArray arr = JSONArray.parseArray(JSON.toJSONString(tree)); //如果直接序列化,时间格式是 时间戳了
        JSONArray arr = JSONArray.parseArray(JSON.toJSONStringWithDateFormat(tree, DatePattern.NORM_DATETIME_PATTERN));
        JSONArray result = filterTree(arr, filterCondition, childNodeName);
        Type listType = new TypeReference<List<T>>() {
        }.getType();
        return JSON.parseObject(result.toJSONString(), listType);
    }

    private JSONArray filterTree(JSONArray tree, Function<JSONObject, Boolean> filterCondition, String childNodeName) {
        Iterator<Object> it = tree.iterator();
        while (it.hasNext()) {
            JSONObject current = (JSONObject) it.next();
            // 把当前节点给到外部,让外部判断是否满足条件
            if (Boolean.TRUE.equals(filterCondition.apply(current))) {
                // 去除子节点 start
                JSONArray childNodes = current.getJSONArray(childNodeName);
                if (!CollUtil.isEmpty(childNodes)) {
                    JSONArray filterTree = filterTree(childNodes, filterCondition, childNodeName);
                    if (CollUtil.isEmpty(filterTree)) {
                        current.put(childNodeName, new JSONArray());
                    }
                }
                // 去除子节点 end
                continue;
            }
            JSONArray childNodes = current.getJSONArray(childNodeName);
            if (!CollUtil.isEmpty(childNodes)) {
                filterTree(childNodes, filterCondition, childNodeName);
            }
            if (CollUtil.isEmpty(childNodes)) {
                it.remove();
            }
        }
        return tree;
    }

2、使用方式

List<ListDto> curTreeFilterResult = filterTree(curTree, currentNode -> {
                    String titleValue = currentNode.getString("title");
                    int serialNumber = currentNode.getIntValue("serialNumber");
                    return StrUtil.contains(titleValue, params.getQuery())
                            || StrUtil.contains(dbDevmProjectInfo.getIdentifier().concat("-" + serialNumber), params.getQuery());
                }, "children");
                if (CollUtil.isNotEmpty(curTreeFilterResult)) {
                    filterTreeResult.add(curTreeFilterResult);
                }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1436478.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot与Elasticsearch版本兼容对比

首先 大家在下载 Elasticsearch 时 最好先弄清楚版本 因为 如果 Spring Boot 版本 不兼容 Elasticsearch 那就是到头一场空了 Elasticsearch 版本 6.x 可以兼容 Spring Boot 2.x Elasticsearch 版本 7.x 可以兼容 Spring Boot 2.x 3.x 4x Elasticsearch 版本 7.x 以及 8.x 可以…

基于SSM的实习管理系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的实习管理系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spri…

安卓动态链接库文件体积优化探索实践

背景介绍 应用安装包的体积影响着用户下载量、安装时长、用户磁盘占用量等多个方面&#xff0c;据Google Play统计&#xff0c;应用体积每增加6MB&#xff0c;安装的转化率将下降1%。 安装包的体积受诸多方面影响&#xff0c;针对dex、资源文件、so文件都有不同的优化策略&…

麒麟信安战略投资湖南超能机器人技术有限公司,加速布局无人智能系统、自主可控机器人操作系统赛道

为进一步完善产业布局&#xff0c;推进战略规划稳步实施&#xff0c;近日&#xff0c;麒麟信安与湖南超能机器人技术有限公司&#xff08;简称“超能机器人”&#xff09;及其原股东签署了《增资协议》及相关配套协议&#xff0c;麒麟信安成为超能机器人股东。 战略投资超能机…

打包 iOS 的 IPA 文件

目录 摘要 引言 准备 选择证书类型 创建应用程序 设置应用程序标识和证书 配置构建设置 构建应用程序 导出IPA 签名和导出 代码案例演示 完成 总结 参考资料 摘要 本篇博客将为您介绍如何打包iOS的IPA文件。从APP提交、创建应用程序、设置应用程序标识和证书、配…

【详解】斗地主随机发牌项目

目录 前言&#xff1a; 1.初始化牌 2.洗牌 3.揭牌 总代码&#xff1a; Card类&#xff1a; CardGame类&#xff1a; Main类&#xff1a; 结语&#xff1a; 前言&#xff1a; 斗地主是全国范围内的一种桌面游戏&#xff0c;本节我们来实现一下斗地主中的简单初始化牌、…

20240202在WIN10下部署faster-whisper

20240202在WIN10下部署faster-whisper 2024/2/2 12:15 前提条件&#xff0c;可以通过技术手段上外网&#xff01;^_ 首先你要有一张NVIDIA的显卡&#xff0c;比如我用的PDD拼多多的二手GTX1080显卡。【并且极其可能是矿卡&#xff01;】800&#xffe5; 2、请正确安装好NVIDIA最…

BEV感知算法学习

BEV感知算法学习 3D目标检测系列 Mono3D(Monocular 3D Object Detection for Autonomous Driving) 流程&#xff1a; 通过在地平面上假设先验&#xff0c;在3D空间中对具有典型物理尺寸的候选边界框进行采样&#xff1b;然后我们将这些方框投影到图像平面上&#xff0c;从而避…

【Vitis】HLS高层次综合的优势

高层次综合 (HLS) 是自动设计进程&#xff0c; 利用数字系统的抽的象行为规范来生成寄存器传输级结构&#xff0c; 以实现给定行为。 使用 HLS 的典型流程包含下列步骤&#xff1a; 1. 围绕给定架构在高抽象层次使用 C/C 编写算法 2. 在行为级别验证功能 3. 使用 HLS 工具为…

(十二)springboot实战——SSE服务推送事件案例实现

前言 SSE&#xff08;Server-Sent Events&#xff0c;服务器推送事件&#xff09;是一种基于HTTP协议的服务器推送技术。它允许服务器向客户端发送异步的、无限长的数据流&#xff0c;而无需客户端不断地轮询或发起请求。这种技术可以用来实现实时通信、在线聊天、即时更新等功…

LeetCode、790. 多米诺和托米诺平铺【中等,二维DP,可转一维】

文章目录 前言LeetCode、790. 多米诺和托米诺平铺【中等&#xff0c;二维DP&#xff0c;可转一维】题目与分类思路二维解法二维转一维 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质…

【VSTO开发-WPS】下调试

重点2步&#xff1a; 1、注册表添加 Windows Registry Editor Version 5.00[HKEY_CURRENT_USER\Software\kingsoft\Office\WPP\AddinsWL] "项目名称"""2、visual studio 运行后&#xff0c;要选中附加到调试&#xff0c;并指定启动项目。 如PPT输入WPP搜…

在 CentOS 7上使用 Apache 和 mod_wsgi 部署 Django 应用的方法

简介 Django 是一个强大的 Web 框架&#xff0c;可以帮助您快速启动 Python 应用程序或网站。Django 包括一个简化的开发服务器&#xff0c;用于在本地测试代码&#xff0c;但对于任何与生产相关的事情&#xff0c;都需要一个更安全和功能强大的 Web 服务器。 在本指南中&…

Python学习路线 - Python高阶技巧 - 拓展

Python学习路线 - Python高阶技巧 - 拓展 闭包闭包注意事项 装饰器装饰器的一般写法(闭包写法)装饰器的语法糖写法 设计模式单例模式工厂模式 多线程进程、线程并行执行多线程编程threading模块 网络编程Socket客户端和服务端Socket服务端编程实现服务端并结合客户端进行测试 S…

毅速集团2023年度总结暨表彰大会圆满举行

2024年2月2日&#xff0c;毅速集团2023年度总结暨表彰大会在上海总部举行&#xff0c;本次年会以“加速世界向增材制造的转变”为主题&#xff0c;全面总结了毅速集团2023年取得的成绩&#xff0c;明确了2024年的发展战略&#xff0c;并对过去一年中表现突出的个人进行了隆重表…

Redis学习及总结

Redis 快速入门 Redis属于非关系型数据库 SQL应用场景 数据结构固定相关业务对数据安全性一致性要求高 NoSQL应用场景 数据结构不固定对一致性&#xff0c;安全性要求不高性能要求高 &#x1f3af;需要使用Xftp 传输压缩包到虚拟机上 安装好Redis后&#xff0c; 执行命令…

第七届西湖论剑·中国杭州网络安全技能大赛 AI 回声海螺 WP

第七届西湖论剑中国杭州网络安全技能大赛-AI-回声海螺 开题&#xff0c;提示输入密码给FLAG。 这个回声海螺应该是个AI&#xff0c;就是复读机&#xff0c;应该是想办法从中骗出密码。 感觉这题不像是AI&#xff0c;也没用啥模型&#xff0c;应该是WEB。或者是说类似于AI的提示…

GLSL ES 1.0

GLSL ES 概述 写在前面 程序是大小写敏感的每一个语句都应该以英文分号结束一个shader必须包含一个main函数&#xff0c;该函数不接受任何参数&#xff0c;并且返回voidvoid main() { }数据值类型 GLSL支持三种数据类型&#xff1a; 整型浮点型&#xff1a;必须包含小数点&…

posix_memalign 与 malloc 对比

1. 原因原理 编程中的类型对齐问题主要是处于性能考虑&#xff0c;如果不做对齐&#xff0c;那么单个数据元素的访问很容易跨在多个时钟周期上&#xff0c;从而导致性能下降。 内建数据类型的对齐&#xff0c;是由编译器和C语言库的API实现中自动完成的&#xff0c;这对于用户是…

LeetCode-第876题-链表的中间结点

1.题目描述 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。如果有两个中间结点&#xff0c;则返回第二个中间结点。 2.样例描述 3.思路描述 创建两个快慢指针 slow , fast &#xff0c;起始共同指向头节点&#xff0c;slow 每次走一步&#xff0c;fas…