vue3 ts 数组构造树形 VS java 数组构造树形

一、背景

vue3 ts 数组构造树形 VS java 数组构造树形,通常是后端返回树形,但是有一些比较吊的后端就是要返回数组,不管前端死活,未雨绸缪,比较前后端 数组组转 树形结构.

二、实现

同个接口,返回同种数据结构.
image

其实有用的就是三个字段 id,name,pid,其他字段只是附加说明.

vue3 ts 数组构造树形

vue3 ts 数组构造树形
getSelectData('/api/revWaterUserDoc/queryRevDivision', '', {}).then((data) => {
      console.log(buildTree(data))  // 返回的数据直接丢到下面方法中,注意data的数据结构
  })


const buildTree = (list:any, pid = 0) =>{ // pid是指对应的父级id,默认为0 也有可能是parentId 为 null 依情况而定. 
  const tree = [];
  for (const item of list) {
    if (item.pid === pid) {
      const children = buildTree(list, item.id);
      if (children.length) {
        item.children = children;
      }
      tree.push(item);
    }
  }
  return tree;
}

image

java 数组构造树形 (java使用hutool实现列表转树形结构)

java使用hutool实现列表转树形结构
// 任何类型的List<T>  形参:
	//list – 源数据集合 parentId – 最顶层父id值 一般为 0 之类 nodeParser – 转换器
	//返回值:
	//List
        List<Tree<Integer>> build = TreeUtil.build(revWaterUserDocPageResult, 0, (node, tree) -> {
            tree.setId(node.getId().intValue());
            tree.setName(node.getName());
            tree.setParentId(node.getPid().intValue());
            tree.putExtra("Extra","除上面那三个默认字段,有额外字段显示,可以这么写");
        });

image

注意点:实体类和工具类中的映射字段要存在,可以手动更改.

image

三种方式组装树形
package me.zhengjie.modules.waterMeter.rest;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author :lcq
 * @Date : 2023/5/8 9:52
 */

/**
 * 使用前提:
 *  主要是根据 反射来 组装树形,所以 要保证 存在下面三个基本属性 id,parentId,children
 */
public class TreeUtil3 {

    /**
     * 通过递归方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByRecursion(List<T> list) {
        return buildTreeByRecursion(list, "id", "parentId", "children");
    }

    /**
     * 通过Map方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByMap(List<T> list) {
        return buildTreeByMap(list, "id", "pid", "children", "0");
    }

    /**
     * 通过两层for循环方式构建树
     *
     * @param list 列表
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByTwoLayersFor(List<T> list) {
        return buildTreeByTwoLayersFor(list, "id", "parentId", "children", "0");
    }

    /**
     * 通过递归方式构建树
     *
     * @param list         列表
     * @param idName       id名称
     * @param parentIdName 父id名称
     * @param childrenName 子节点列表名称
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByRecursion(List<T> list, String idName, String parentIdName, String childrenName) {
        if (StringUtils.isBlank(idName) || StringUtils.isBlank(parentIdName) || StringUtils.isBlank(childrenName)) {
            return new ArrayList<>();
        }
        List<T> returnList = new ArrayList<>();
        //获取list中所有的主键id
        List<String> ids = list.stream().map(o -> getFieldValue(o, idName).toString()).collect(Collectors.toList());

        for (T t : list) {
            String parentId = getFieldValue(t, parentIdName).toString();
            //如果是顶级节点, 遍历该父节点的所有子节点,因为顶层的parentId为0
            if (!ids.contains(parentId)) {
                buildTreeRecursive(list, t, idName, parentIdName, childrenName);
                returnList.add(t);
            }
        }
        if (returnList.isEmpty()) {
            returnList = list;
        }
        return returnList;
    }

    /**
     * 递归fn
     *
     * @param list 分类表
     * @param node 子节点
     */
    private static <T> void buildTreeRecursive(List<T> list, T node, String idName, String parentIdName, String childrenName) {
        // 得到子节点列表
        List<T> childList = getChildList(list, node, idName, parentIdName);
        setFieldValue(node, childList, childrenName);
        for (T child : childList) {
            if (hasChild(list, child, idName, parentIdName)) {
                buildTreeRecursive(list, child, idName, parentIdName, childrenName);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private static <T> List<T> getChildList(List<T> list, T node, String idName, String parentIdName) {
        List<T> tlist = new ArrayList<>();
        String oId = getFieldValue(node, idName).toString();
        for (T child : list) {
            String tParentId = getFieldValue(child, parentIdName).toString();
            if (tParentId.equals(oId)) {
                tlist.add(child);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private static <T> boolean hasChild(List<T> list, T t, String idName, String parentIdName) {
        return getChildList(list, t, idName, parentIdName).size() > 0;
    }

    /**
     * 通过Map方式构建树
     *
     * @param list           列表
     * @param idName         id名称
     * @param parentIdName   父id名称
     * @param childrenName   子节点列表名称
     * @param topParentIdVal 顶层节点父id的值
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByMap(List<T> list, String idName, String parentIdName, String childrenName, String topParentIdVal) {
        if (StringUtils.isBlank(idName) || StringUtils.isBlank(parentIdName) || StringUtils.isBlank(childrenName)) {
            return new ArrayList<>();
        }
        //根据parentId进行分组
        Map<String, List<T>> mapList = list.stream().collect(Collectors.groupingBy(o -> getFieldValue(o, parentIdName).toString()));
        //给每个节点设置子节点列表
        list.forEach(node -> setFieldValue(node, mapList.get(getFieldValue(node, idName).toString()), childrenName));
        return list.stream().filter(o -> topParentIdVal.equals(getFieldValue(o, parentIdName))).collect(Collectors.toList());
    }

    /**
     * 获取属性值
     *
     * @param o         对象
     * @param fieldName 属性名
     * @return {@link String}
     */
    private static Object getFieldValue(Object o, String fieldName) {
        try {
            Class<?> oClass = o.getClass();
            Field field = oClass.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(o);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置字段值
     *
     * @param o         对象
     * @param val       值
     * @param fieldName 属性名
     */
    private static void setFieldValue(Object o, Object val, String fieldName) {
        try {
            Class<?> oClass = o.getClass();
            Field field = oClass.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(o, val);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过两层for循环方式构建树
     *
     * @param list           列表
     * @param idName         id名称
     * @param parentIdName   父id名称
     * @param childrenName   子节点列表名称
     * @param topParentIdVal 顶层节点父id的值
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> buildTreeByTwoLayersFor(List<T> list, String idName, String parentIdName, String childrenName, String topParentIdVal) {
        List<T> resultList = new ArrayList<>();
        for (T node : list) {
            //如果是顶层节点
            if (topParentIdVal.equals(getFieldValue(node, parentIdName))) {
                resultList.add(node);
            }
            for (T child : list) {
                if (getFieldValue(child, parentIdName).equals(getFieldValue(node, idName))) {
                    List<T> childrenList = (List<T>) getFieldValue(node, childrenName);
                    if (CollectionUtils.isEmpty(childrenList)) {
                        childrenList = new ArrayList<>();
                        setFieldValue(node, childrenList, childrenName);
                    }
                    childrenList.add(child);
                }
            }
        }
        return resultList;
    }

}

结果:

------效果1-buildTreeByMap-----
image
image

------效果2-buildTreeByRecursion-----
image
image

------效果3 -buildTreeByTwoLayersFor-----
image
image

三、遇到的报错

1.实体类和工具类映射的字段不一致,如children 实体类没有.还有就是工具类parentId 但是实体类为Pid ,字段名称不一致.

四、参考博客

https://blog.csdn.net/qq_46258463/article/details/130562711

posted @ 2023-09-20 16:59  林财钦  阅读(229)  评论(0)    收藏  举报