0

0

JS如何实现分组功能

星降

星降

发布时间:2025-08-18 11:36:02

|

952人浏览过

|

来源于php中文网

原创

使用reduce方法可高效实现JS数据分组,通过遍历数组并以指定键累积分组结果,支持处理嵌套属性、复合键、键值缺失及类型不一致等复杂场景,结合Map或分批处理可进一步优化性能。

js如何实现分组功能

JavaScript中实现分组功能,核心思想其实就是遍历你手头的数据集合,然后根据你预设的一个“规则”或者说“键”,把那些符合相同规则的数据项归拢到一起。说白了,就是把散落在各处的数据,按某种共同点整理成一个个小堆。最常用、也最灵活的实现方式,我个人觉得是利用

Array.prototype.reduce()
方法。

解决方案

要用JS实现分组,最直接且高效的方式是利用数组的

reduce
方法。它允许你遍历数组,并累积一个结果。

假设我们有一组用户数据,想根据他们的城市进行分组:

const users = [
    { id: 1, name: 'Alice', city: 'New York' },
    { id: 2, name: 'Bob', city: 'London' },
    { id: 3, name: 'Charlie', city: 'New York' },
    { id: 4, name: 'David', city: 'Paris' },
    { id: 5, name: 'Eve', city: 'London' },
];

// 使用 reduce 实现分组
const groupedByCity = users.reduce((accumulator, currentUser) => {
    const city = currentUser.city; // 获取分组的键
    if (!accumulator[city]) {
        accumulator[city] = []; // 如果这个城市还没有对应的数组,就创建一个
    }
    accumulator[city].push(currentUser); // 把当前用户添加到对应的城市数组中
    return accumulator; // 返回累积器,供下一次迭代使用
}, {}); // 初始值是一个空对象

console.log(groupedByCity);
/*
输出大致会是这样:
{
  "New York": [
    { id: 1, name: 'Alice', city: 'New York' },
    { id: 3, name: 'Charlie', city: 'New York' }
  ],
  "London": [
    { id: 2, name: 'Bob', city: 'London' },
    { id: 5, name: 'Eve', city: 'London' }
  ],
  "Paris": [
    { id: 4, name: 'David', city: 'Paris' }
  ]
}
*/

这个过程有点像在整理文件:你拿到一份文件(

currentUser
),看一眼它的分类标签(
currentUser.city
),然后找到对应的文件夹(
accumulator[city]
)。如果文件夹不存在,就先创建一个新的(
!accumulator[city]
),再把文件放进去。这个
accumulator
就是你最终的分组结果。

如何处理复杂或多层级的数据分组需求?

实际开发中,数据结构往往比简单的扁平对象要复杂得多。你可能需要根据嵌套属性分组,或者同时依据多个条件来分组。

对于多层级的数据,例如

user.address.country
,你只需要调整获取键的逻辑:

const usersWithAddress = [
    { id: 1, name: 'Alice', address: { city: 'New York', country: 'USA' } },
    { id: 2, name: 'Bob', address: { city: 'London', country: 'UK' } },
    { id: 3, name: 'Charlie', address: { city: 'Boston', country: 'USA' } },
    { id: 4, name: 'David', address: { city: 'Paris', country: 'France' } },
];

const groupedByCountry = usersWithAddress.reduce((acc, user) => {
    const country = user.address?.country; // 使用可选链操作符,防止 address 不存在
    if (country) { // 确保国家存在才进行分组
        if (!acc[country]) {
            acc[country] = [];
        }
        acc[country].push(user);
    }
    return acc;
}, {});

console.log(groupedByCountry);

这里我加入了

?.
可选链操作符和
if (country)
判断,这是个好习惯,能避免在数据结构不完全一致时报错。

而当需要根据多个条件组合分组时,你可以拼接一个复合键。比如,要同时根据城市和国家分组:

const groupedByCityAndCountry = usersWithAddress.reduce((acc, user) => {
    const city = user.address?.city;
    const country = user.address?.country;
    if (city && country) {
        const compositeKey = `${city}-${country}`; // 创建复合键
        if (!acc[compositeKey]) {
            acc[compositeKey] = [];
        }
        acc[compositeKey].push(user);
    }
    return acc;
}, {});

console.log(groupedByCityAndCountry);
/*
输出示例:
{
  "New York-USA": [ { ...Alice... } ],
  "London-UK": [ { ...Bob... } ],
  "Boston-USA": [ { ...Charlie... } ],
  "Paris-France": [ { ...David... } ]
}
*/

这种复合键的方式非常灵活,你可以根据任意多的属性来生成唯一的键,实现更细粒度的分组。

在分组过程中,如何处理键值缺失或数据类型不一致的情况?

这是个很现实的问题,数据往往不那么“干净”。如果分组的键值可能缺失(

null
,
undefined
)或者数据类型不一致(比如有时是数字ID,有时是字符串ID),你需要增加一些健壮性判断。

处理键值缺失: 当某个数据项用于分组的键可能不存在时,如果不做处理,

accumulator[undefined]
accumulator[null]
可能会出现,这通常不是你想要的结果。

const products = [
    { id: 1, name: 'Laptop', category: 'Electronics' },
    { id: 2, name: 'Mouse', category: 'Electronics' },
    { id: 3, name: 'Keyboard' }, // 缺少 category
    { id: 4, name: 'Monitor', category: null }, // category 为 null
    { id: 5, name: 'Headphones', category: 'Electronics' }
];

const groupedBySafeCategory = products.reduce((acc, product) => {
    // 优先使用实际的 category,如果缺失或为 null/undefined,则使用 'Other'
    const category = product.category || 'Other'; 

    if (!acc[category]) {
        acc[category] = [];
    }
    acc[category].push(product);
    return acc;
}, {});

console.log(groupedBySafeCategory);
/*
输出示例:
{
  "Electronics": [ { ...Laptop... }, { ...Mouse... }, { ...Headphones... } ],
  "Other": [ { ...Keyboard... }, { ...Monitor... } ]
}
*/

这里我用了

product.category || 'Other'
,这是一个常见的短路求值技巧,当
product.category
是 falsy 值(
undefined
,
null
,
''
,
0
,
false
)时,它会回退到
'Other'
。这样,所有没有明确分类的产品都会被归到“Other”组。

GentleAI
GentleAI

GentleAI是一个高效的AI工作平台,为普通人提供智能计算、简单易用的界面和专业技术支持。让人工智能服务每一个人。

下载

处理数据类型不一致: 如果你的分组键可能出现类型不一致,比如数字

1
和字符串
"1"
,它们在作为对象键时会被视为不同的键。为了确保它们被归为一类,你需要进行类型转换:

const items = [
    { id: 1, type: 100 },
    { id: 2, type: '100' }, // 注意这里是字符串
    { id: 3, type: 200 }
];

const groupedByTypeConsistent = items.reduce((acc, item) => {
    // 将 type 统一转换为字符串,确保键的唯一性
    const typeKey = String(item.type); 

    if (!acc[typeKey]) {
        acc[typeKey] = [];
    }
    acc[typeKey].push(item);
    return acc;
}, {});

console.log(groupedByTypeConsistent);
/*
输出示例:
{
  "100": [ { id: 1, type: 100 }, { id: 2, type: '100' } ],
  "200": [ { id: 3, type: 200 } ]
}
*/

通过

String(item.type)
,无论是数字还是字符串,都会被统一转换为字符串作为对象的键,从而避免了因类型不同而导致的重复分组。

对于大量数据,JS分组操作的性能考量与优化策略有哪些?

在处理大量数据时,性能总是值得关注的话题。对于JavaScript中的分组操作,特别是使用

reduce
这种单次遍历的方式,它的时间复杂度通常是 O(n),其中 n 是数组的长度。这意味着处理的数据量越大,所需时间就越长,但它是线性增长的,效率相对较高。

内存消耗: 分组操作会创建一个新的对象来存储分组后的数据。这个新对象的大小取决于原始数据的数量和分组的粒度。如果分组后的键非常多,或者每个组内的数据项非常多,那么这个结果对象可能会占用大量内存。在浏览器环境中,过大的内存占用可能导致页面卡顿甚至崩溃。

优化策略:

  1. 避免不必要的计算:

    reduce
    的回调函数内部,确保只进行必要的计算。比如,如果分组键可以提前计算好,就不要在每次迭代中重复计算。不过,通常情况下,获取一个属性值并不会造成显著的性能瓶颈。

  2. 合理选择数据结构:

    • 普通对象 (
      {}
      ):
      最常用,键只能是字符串或Symbol。对于大多数情况已经足够。
    • Map
      对象:
      如果你的分组键是非字符串类型(例如,希望用对象或数字作为键而不进行类型转换),或者你预期会有非常多的分组键,
      Map
      可能会比普通对象有轻微的性能优势,因为它在内部管理键值对的方式更优化。
    // 使用 Map 进行分组,键可以是任意类型
    const dataWithMixedKeys = [
        { id: 1, groupKey: { a: 1 } },
        { id: 2, groupKey: { a: 1 } }, // 注意:这里是不同的对象实例,Map 会视为不同的键
        { id: 3, groupKey: 100 },
        { id: 4, groupKey: '100' }
    ];
    
    const groupedByMap = dataWithMixedKeys.reduce((map, item) => {
        const key = item.groupKey;
        if (!map.has(key)) {
            map.set(key, []);
        }
        map.get(key).push(item);
        return map;
    }, new Map());
    
    console.log(groupedByMap);
    // 注意:由于 {a:1} 是两个不同的对象实例,它们会被视为两个不同的键
    // Map(4) {
    //   { a: 1 } => [ { id: 1, groupKey: { a: 1 } } ],
    //   { a: 1 } => [ { id: 2, groupKey: { a: 1 } } ],
    //   100 => [ { id: 3, groupKey: 100 } ],
    //   '100' => [ { id: 4, groupKey: '100' } ]
    // }

    对于对象作为键的情况,

    Map
    会比较引用地址,所以两个内容相同的对象如果不是同一个引用,也会被视为不同的键。这需要你根据实际需求来判断是否适用。

  3. 分批处理 (Batch Processing): 如果数据量极其庞大,导致一次性处理会阻塞主线程(在浏览器中表现为页面卡顿),可以考虑将数据分批处理。例如,使用

    setTimeout
    requestAnimationFrame
    将处理任务分解成多个小块,在不同的事件循环周期中执行。但这会增加代码复杂度,通常只在极端情况下考虑。

  4. 服务器端处理: 对于百万级别甚至千万级别的数据,前端JS进行分组操作通常是不现实的,也并非其设计初衷。这种情况下,数据处理应该在服务器端完成,由数据库或后端服务提供聚合好的数据。

总的来说,对于前端JS能处理的数据量(几万到几十万条),

Array.prototype.reduce()
的效率通常是足够的。主要的性能瓶颈往往不在于
reduce
本身,而在于数据量过大导致的内存占用,或者你在
reduce
回调中执行了非常复杂的、耗时的操作。在优化之前,先进行性能分析,找出真正的瓶颈所在。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

338

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

225

2025.10.31

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

138

2026.02.12

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1051

2023.08.02

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

254

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1089

2024.03.01

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

847

2023.08.22

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

761

2023.08.03

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
如何进行WebSocket调试
如何进行WebSocket调试

共1课时 | 0.1万人学习

TypeScript全面解读课程
TypeScript全面解读课程

共26课时 | 5.1万人学习

前端工程化(ES6模块化和webpack打包)
前端工程化(ES6模块化和webpack打包)

共24课时 | 5.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号