List优雅分组

一、前言

        最近小永哥发现,在开发过程中,经常会遇到需要对list进行分组,就是假如有一个RecordTest对象集合,RecordTest对象都有一个type的属性,需要将这个集合按type属性进行分组,转换为一个以type为key,RecordTest集合为value的Map对象,这个功能其实本身并不难,相信以老铁们的实力那还不是轻轻松松嘛,那下面小永哥就先献丑了。

二、代码实现

2.1、常规实现

        实现思路:通过创建一个Map,然后遍历原RecordTest集合,在循环中先获取到type属性,然后判断该type属性在Map中是否存在,如果以存在,就从Map中通过key获取到一个list,然后将当前本次循环所操作的RecordTest对象add到这个list中。如果该type在Map中不存在,那么就新建一个list,将本次循环的RecordTest对象add进去,然后再以type为key,list为value的方式put进Map中,最后我们就获取到了一个以type分好组的Map对象了。

package com.relation;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Map;

/**
 * @author huhy
 * @version 1.0
 * @Description:
 * @ClassName Date:2025/5/20 21:50
 */
public class ListTest {

    @Test
    public void listGroupTest(){
        //模拟数据准备
        List<RecordTest> recordTestList = CollectionUtil.newArrayList();
        recordTestList.add(new RecordTest("型号A","item-code-A","item"));
        recordTestList.add(new RecordTest("型号B","item-code-B","item"));
        recordTestList.add(new RecordTest("文件A","file-code-B","file"));
        recordTestList.add(new RecordTest("文件B","file-code-B","file"));
        recordTestList.add(new RecordTest("文件C","file-code-C","file"));
        recordTestList.add(new RecordTest("图片A","photo-code-A","photo"));
        recordTestList.add(new RecordTest("视频A","video-code-C","video"));
        //按type属性进行分组
        Map<String,List<RecordTest>> recordTestGroupMap = MapUtil.newHashMap();
        for (RecordTest recordTest : recordTestList) {
            String type = recordTest.getType();
            List<RecordTest> recordTests;
            if(recordTestGroupMap.containsKey(type)){
                recordTests = recordTestGroupMap.get(type);
            }else {
                recordTests = CollectionUtil.newArrayList();
                recordTestGroupMap.put(type,recordTests);
            }
            recordTests.add(recordTest);
        }
        //打印
        System.out.println(JSON.toJSONString(recordTestGroupMap));
    }

    class RecordTest{
        private String name;
        private String code;
        private String type;

        public RecordTest(String name, String code, String type) {
            this.name = name;
            this.code = code;
            this.type = type;
        }

        public String getName() {
            return name;
        }

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

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }

}

        

        打印出的信息不太方便观看,我们找个工具将JSON格式化一下,顺便提一句,有时候我们需要将JSON文本格式化的时候,是不是冷不丁还想不到什么好的方式,特别是开发环境还是内网的时候,没办法上网在线转?其实不管是内网还是外网开发环境,我们作为一个后端开发人员,postman可以说是标配,毕竟还得自测接口嘛,这时候postman就能发挥作用了,postman的post请求在输入参数以后,有一个Beautify的按钮,轻轻一点,JSON文本就格式化好了,非常方便。

        这点小玩意儿虽然顺利实现了,而且实现代码也不孬,但是距离优雅还是差不少,下面我们换种嗨皮的实现方式。

2.2、优雅实现

        

@Test
    public void listGroupTest(){
        //模拟数据准备
        List<RecordTest> recordTestList = CollectionUtil.newArrayList();
        recordTestList.add(new RecordTest("型号A","item-code-A","item"));
        recordTestList.add(new RecordTest("型号B","item-code-B","item"));
        recordTestList.add(new RecordTest("文件A","file-code-B","file"));
        recordTestList.add(new RecordTest("文件B","file-code-B","file"));
        recordTestList.add(new RecordTest("文件C","file-code-C","file"));
        recordTestList.add(new RecordTest("图片A","photo-code-A","photo"));
        recordTestList.add(new RecordTest("视频A","video-code-C","video"));
        //按type属性进行分组
        Map<String,List<RecordTest>> recordTestGroupMap = recordTestList.stream().collect(Collectors.groupingBy(RecordTest::getType));
        //打印
        System.out.println(JSON.toJSONString(recordTestGroupMap));
    }

三、结语

        通过jdk1.8特性一行实现分组功能,代码量瞬间减少了10行,虽然现在节省的10行不算什么,但是积少成多,等真正开发的时候,我们可以把学到的东西都尽可能的付诸于实践,这样节省的就不仅仅是10行,可能就是成千上万行,我们的代码也会越来越精炼。

<think>我们正在讨论前端开发中实现数据分组与汇总的方法。根据提供的引用,我们可以总结出几种常见的技术手段。以下将详细介绍这些方法,并提供代码示例。 ### 方法1: 使用对象映射进行分组(引用[1]和[引用2]) 这种方法利用JavaScript对象的键值对特性,通过遍历数组,以某个属性作为键,将相同键值的元素归为一组。 **步骤**: 1. 创建一个空对象(作为映射)和一个空数组(存放结果)。 2. 遍历原数组,检查当前元素的指定属性值是否已作为键存在于映射对象中。 - 如果不存在,则创建一个新的分组对象,并添加到结果数组中,同时在映射对象中记录。 - 如果存在,则将当前元素添加到对应的分组中。 **示例代码**(按`shopid`分组): ```javascript const orders = [ /* 原始订单数据 */ ]; const grouped = {}; const result = []; orders.forEach(item => { const key = item.shopid; if (!grouped[key]) { grouped[key] = { shopid: item.shopid, shopname: item.shopname, order_list: [item], // 初始化数组并放入当前项 order_nums: 0, // 后续可汇总 actual_payment: 0 // 后续可汇总 }; result.push(grouped[key]); } else { grouped[key].order_list.push(item); } }); // 汇总:计算每个分组的订单数量和总支付金额 result.forEach(group => { group.order_nums = group.order_list.length; group.actual_payment = group.order_list.reduce((sum, order) => sum + order.actual_payment, 0); }); ``` ### 方法2: 使用`Array.prototype.reduce`(更优雅的方式) `reduce`方法可以更简洁地实现分组,同时进行汇总计算。 **示例代码**(按`shopid`分组并汇总): ```javascript const result = orders.reduce((acc, item) => { const key = item.shopid; if (!acc[key]) { acc[key] = { shopid: item.shopid, shopname: item.shopname, order_list: [], order_nums: 0, actual_payment: 0 }; } acc[key].order_list.push(item); acc[key].order_nums += 1; // 或者最后统一计算 acc[key].actual_payment += item.actual_payment; return acc; }, {}); // 如果需要数组形式,再转换 const resultArray = Object.values(result); ``` ### 方法3: 按固定数量分组(引用[3]) 有时需要将数组按固定数量分组(如每5个一组),可以使用循环和`slice`方法。 **示例代码**: ```javascript function chunkArray(arr, chunkSize) { const result = []; for (let i = 0; i < arr.length; i += chunkSize) { result.push(arr.slice(i, i + chunkSize)); } return result; } const groupedArray = chunkArray(arr, 5); // 每5个一组 ``` ### 方法4: 使用ES6 Map(适用于复杂键) 当分组键不是字符串(如对象、函数等)时,可以使用Map。 **示例代码**: ```javascript const map = new Map(); orders.forEach(item => { const key = item.shopid; if (!map.has(key)) { map.set(key, { shopid: key, shopname: item.shopname, order_list: [], order_nums: 0, actual_payment: 0 }); } const group = map.get(key); group.order_list.push(item); group.order_nums += 1; group.actual_payment += item.actual_payment; }); const result = Array.from(map.values()); ``` ### 总结 前端数据分组与汇总的常用方法包括: 1. 使用普通对象作为映射进行分组(适合键为字符串或数字) 2. 使用`reduce`方法简化分组和汇总过程 3. 使用循环和`slice`按固定数量分组 4. 使用Map处理复杂键的分组 选择哪种方法取决于具体需求和数据特点。对于简单的分组汇总,`reduce`方法通常代码更简洁;对于按数量分组,则使用循环切片。 §§相关问题§§ 1. 如何在前端实现多级分组(如先按店铺分组,再按配送点分组)? 2. 使用`reduce`方法分组时,如何动态生成汇总字段? 3. 如何在前端高效处理大量数据的分组与汇总(性能优化)? 4. 在Vue或React中,如何将分组后的数据绑定到视图?</think>### 前端数据分组与汇总的实现方法 在前端开发中,数据分组与汇总是常见需求,以下是几种核心实现方法及代码示例: #### 一、基础分组方法 1. **对象映射法**(最常用) 利用对象键的唯一性进行分组,适合按ID等唯一值分组: ```javascript const data = [{shopid:1, amount:100}, {shopid:1, amount:200}, {shopid:2, amount:150}]; const grouped = {}; data.forEach(item => { const key = item.shopid; if (!grouped[key]) { grouped[key] = { shopid: key, items: [], total: 0 }; } grouped[key].items.push(item); grouped[key].total += item.amount; // 实时汇总 }); const result = Object.values(grouped); ``` 2. **reduce() 法**(函数式编程) 更简洁的链式操作: ```javascript const result = data.reduce((acc, item) => { const key = item.shopid; acc[key] = acc[key] || { ...item, items: [], total: 0 }; acc[key].items.push(item); acc[key].total += item.amount; return acc; }, {}); ``` #### 二、多级分组 嵌套实现多维度分组(如先按店铺,再按配送点): ```javascript const multiGroup = data.reduce((acc, item) => { const shopKey = item.shopid; const pointKey = item.peisongdianId; acc[shopKey] = acc[shopKey] || {}; acc[shopKey][pointKey] = acc[shopKey][pointKey] || { shopname: item.shopname, peisongdianName: item.peisongdianName, items: [], total: 0 }; const group = acc[shopKey][pointKey]; group.items.push(item); group.total += item.amount; return acc; }, {}); ``` #### 三、固定数量分组 将数组按指定长度分块(如每5个一组): ```javascript function chunkArray(arr, size) { return Array.from( { length: Math.ceil(arr.length / size) }, (_, i) => arr.slice(i * size, i * size + size) ); } const grouped = chunkArray(data, 5); // 每5条一组 ``` #### 四、动态条件分组 根据运行时条件动态分组: ```javascript function dynamicGroup(arr, keyFn) { return arr.reduce((groups, item) => { const key = keyFn(item); // 动态生成分组键 groups[key] = groups[key] || []; groups[key].push(item); return groups; }, {}); } // 示例:按金额区间分组 const priceGroups = dynamicGroup(data, item => item.amount > 200 ? "high" : item.amount > 100 ? "medium" : "low" ); ``` ### 性能优化建议 1. **大数据量处理**: - 使用 `Web Worker` 避免阻塞UI - 分页分组(先切片再分组) 2. **减少内存占用**: ```javascript // 只存储必要字段 grouped[key] = { id: item.id, count: (grouped[key]?.count || 0) + 1 // 仅计数不存完整对象 }; ``` 3. **使用Map替代Object** 当分组键为对象等非字符串时: ```javascript const map = new Map(); map.set(key, [...(map.get(key) || []), item]); ``` ### 典型应用场景 1. 电商订单按店铺/配送点分组 2. 数据可视化中的分类统计 3. 表格数据的分组展示 4. 无限滚动列表的分页加载 > **关键点**:选择方法时需考虑数据规模(小数据用`reduce`,大数据用分块处理)、分组复杂度(单级/多级)和输出格式要求(对象/数组)[^1][^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值