JavaScript数组按属性分组:从reduce手写到Map安全实现与性能避坑
做开发这几年“JSON 数组按某个属性分组”绝对是我见过最常被问到的需求之一。后端丢过来一百条订单前端要按日期分成几块展示导出报表的时候要把同部门的数据合并到一起甚至只是写个数据清洗脚本也绕不开把同类项归到一堆的操作。最早我也是一通 for 循环加 if 判断代码写得又臭又长还容易漏后来才彻底换成reduceMap的组合顺带把各种奇葩边界情况踩了一遍。这篇文章就围绕这个场景展开把分组、合并、聚合、性能、避坑一次说透。适合刚接触 JavaScript 数组处理的新手也适合写了好几年代码但偶尔还会在分组问题上栽跟头的朋友。1. 先想清楚分组后的输出格式对象、Map 还是嵌套数组很多人拿到需求就直接写循环写到一半才发现“合并到一块”这四个字其实很模糊。属性值相同的项放在一起之后你到底需要一个什么样的结果这个决定了整个代码的结构也是最容易返工的地方。常见的输出形态有三种我一般先根据下游数据怎么消费来决定输出形态代码形态适用场景对象字典{ 研发部: [item1, item2], 市场部: [item3] }前端按 key 直接取列表或需要随机读取某个分组MapMap { 研发部 [item1, item2] }需要频繁增删分组、key 可能含危险字符时首选嵌套数组[{ key: 研发部, list: [item1, item2] }]需要遍历渲染表格或要把分组结果传给图表组件如果你只是要把分组结果丢给一个下拉组件那第三种最合适因为 Vue 或者 React 渲染列表时天然喜欢数组。如果你是要拿某个固定 key 的数据做后续计算比如grouped[2025-01-06]那对象字典最直接。如果你后端返回的字段里属性的取值可能会包含__proto__、constructor这类特殊字符串那对象字典会出大问题这个后面专门讲。我现在的习惯是动手写分组函数之前先花一分钟问自己两个问题——下游消费端要数组还是字典分组字段的类型是否已经完全统一这两个问题没想清楚后面十有八九要推翻重写。特别是日期字段同一个接口里今天返回2025-01-06明天返回2025/01/06分组结果就是两个组这个我在第 6 节会详细展开。2. 核心实现从 reduce 手写分组到安全版2.1 最简单的 reduce 版本网上搜“JavaScript 数组分组”90% 的方案长这样const input [ { name: 张三, dept: 研发部, score: 88 }, { name: 李四, dept: 市场部, score: 76 }, { name: 王五, dept: 研发部, score: 92 }, ]; const grouped input.reduce((acc, item) { const key item.dept; if (!acc[key]) { acc[key] []; } acc[key].push(item); return acc; }, {}); console.log(grouped); // { // 研发部: [ { name: 张三, ... }, { name: 王五, ... } ], // 市场部: [ { name: 李四, ... } ] // }这段代码在普通业务场景下完全能跑。reduce的优势在于把“遍历 累加”收敛成一次链式调用acc就是最终要输出的结构。核心逻辑就是先判断当前属性值的分组在不在结果里不在就初始化一个空数组再把当前项推进去。但如果你只是抄了这一版就上线后面会遇到一些非常隐蔽的问题。我见过有人用这个写法跑了半年直到某天后端某条数据的 dept 字段恰好是__proto__整个分组结果直接乱了排查了很久才定位到是原型链被污染了。2.2 原型链污染__proto__和constructor为什么是雷用普通对象{}当分组容器本质上是在给这个对象动态添加属性。对象自带原型链当你执行acc[key]的时候JavaScript 引擎会先查自身属性再顺着原型链往上找。考虑这么一组数据const input [ { name: A, dept: __proto__, score: 100 }, { name: B, dept: 研发部, score: 90 }, ]; const grouped input.reduce((acc, item) { const key item.dept; if (!acc[key]) { acc[key] []; } acc[key].push(item); return acc; }, {});第一步acc[__proto__]访问到的不是普通属性而是Object.prototype的__proto__访问器。你给它赋一个数组实际上是在尝试修改这个对象实例的原型链结果自身属性根本不会被创建。这一步本来就够乱了后面如果你再拿grouped[__proto__].push(...)操作内部会直接报错或者出现不可预期行为。再看constructorconst grouped {}; // grouped.constructor 默认等于 Object 构造函数 // if (!grouped.constructor) 的结果是 false当你分组字段恰好是constructor时if (!acc[key])判断不会成立因为你访问到的是原型链上的Object构造函数这是一个 truthy 值于是这个分组永远无法初始化。结果就是构造分组的操作被跳过了。这就是我一直强调不要用普通对象当字典的原因。要防这个问题最简单的办法是换成Map彻底绕开原型链。2.3 Map 版本安全且顺序稳定Map作为分组容器有两个明显优势。第一完全不受原型链影响任何字符串都能作为 key。第二Map严格按照插入顺序保存元素而普通对象的 key 在整数键场景下会按照从小到大自动排序可能打乱你希望保持的原始顺序。function groupBy(arr, keySelector) { const map new Map(); for (const item of arr) { const key keySelector(item); if (!map.has(key)) { map.set(key, []); } map.get(key).push(item); } return map; } const input [ { name: 张三, dept: 研发部, score: 88 }, { name: 李四, dept: 市场部, score: 76 }, { name: 王五, dept: 研发部, score: 92 }, ]; const groupedMap groupBy(input, item item.dept); for (const [dept, list] of groupedMap) { console.log(dept, list.length); } // 研发部 2 // 市场部 1我把分组函数单独抽出来了因为实际开发里它会被复用很多次。keySelector 设计成一个函数意味着你可以自由决定“属性”怎么取——可以是item.dept也可以是item.user.id也可以是item.tags.join(,)。这个后面会重点展开。如果你确实需要把Map再转成普通对象或数组可以用// Map 转数组 const arrResult [...groupedMap].map(([key, list]) ({ key, list })); // Map 转普通对象不推荐除非你确定 key 很安全 const objResult Object.fromEntries(groupedMap);Object.fromEntries很方便但同样有原型链污染的隐患所以我几乎不用它转分组结果宁可多写一层map生成数组。3. 复杂分组实战扩展多属性分组、去重、动态归并3.1 多属性分组用组合键实际需求往往不是“按一个属性分组”这么简单。比如销售数据要按“年份 季度”分组考勤记录要按“日期 部门”分组。组合键的思路是把多个属性拼接成一个字符串作为 Map 的 key。const sales [ { year: 2024, quarter: Q1, amount: 12000, region: 华东 }, { year: 2024, quarter: Q1, amount: 8000, region: 华南 }, { year: 2024, quarter: Q2, amount: 15000, region: 华东 }, { year: 2023, quarter: Q1, amount: 9000, region: 华东 }, ]; const grouped groupBy(sales, item ${item.year}-${item.quarter}); for (const [key, list] of grouped) { console.log(key, list.length, list.reduce((sum, s) sum s.amount, 0)); } // 2024-Q1 2 20000 // 2024-Q2 1 15000 // 2023-Q1 1 9000这里有个细节组合 key 的分隔符尽量选一个业务数据里不会出现的字符我用::或|比较多。如果你用-而年份字段本身可能带负号比如2024-01这种就会解析出歧义。虽然只是作为 Map 的 key 不需要解析回去但调试的时候看 key 就很难受所以选一个明显的分隔符能省不少事。3.2 分组之后还要做什么合并字段、去重、聚合统计“属性值相同的合并到一块”这句话在不同场景下的含义完全不同。我拆成三种情况第一种是纯粹分组每组是一个数组保留所有原始项。 第二种是同 key 的项需要做字段合并比如按设备 ID 分组每个设备有多条上报记录我只想保留最新的一条或者把多条数据的数值字段相加。 第三种是分组后做聚合不保留原始数组直接输出统计结果。// 按 id 分组保留每个 id 的最新记录 const records [ { id: 1, status: pending, ts: 1700000001 }, { id: 1, status: done, ts: 1700000005 }, { id: 2, status: pending, ts: 1700000002 }, ]; const latestByUser new Map(records.map(item [item.id, item])); // 这里用了 Map 的 key 唯一性后面出现的项会覆盖前面的项 // 但前提是你的数组已经按时间从旧到新排好了否则不保证“最新” console.log([...latestByUser.values()]); // [ // { id: 1, status: done, ts: 1700000005 }, // { id: 2, status: pending, ts: 1700000002 } // ]聚合统计更常见。我要算每个部门的总分和人数可以这样const stats new Map(); for (const item of input) { const key item.dept; const cur stats.get(key) ?? { dept: key, totalScore: 0, count: 0 }; cur.totalScore item.score; cur.count 1; stats.set(key, cur); } const result [...stats.values()]; // [ // { dept: 研发部, totalScore: 180, count: 2 }, // { dept: 市场部, totalScore: 76, count: 1 } // ]用??空值合并运算符来初始化当前分组代码会简洁很多。注意cur是从stats.get(key)拿到的同一个对象引用totalScore累加的操作会直接体现在Map里的对象上最后[...stats.values()]取到的就是已经累加完毕的结果。3.3 keySelector 函数化从按属性分组升级到按任意规则分组我在 2.3 里已经把分组函数抽成groupBy(arr, keySelector)这个抽象非常值钱。它的意义在于调用方想按什么分组都行。// 按首字母分组 const users [ { name: Alice }, { name: Bob }, { name: Charlie }, { name: Amy }, ]; const byInitial groupBy(users, item item.name.charAt(0)); // Map { A [Alice, Amy], B [Bob], C [Charlie] } // 按成绩区间分组 const byLevel groupBy(input, item item.score 90 ? 优秀 : item.score 60 ? 及格 : 不及格 ); // 按嵌套字段分组 const byCity groupBy(usersWithAddress, item item.address.city);一组数据可以同时被不同规则分组这归功于 keySelector 是一个参数。如果你硬编码成item item.dept那每次换个需求都要重新拷贝一份函数代码量会膨胀。把这个函数抽出来之后分组器本身不再关心你的业务字段是什么它只负责“算 key分桶”。4. 性能、内存与排序数据量大时怎么处理分组功能写出来容易但数据量一上来很多隐含问题就浮现了。我处理过最大的一个数组有几十万条日志要按小时分桶做统计当时对比了几种实现之后才确定方案。4.1 for...of 还是 reduce性能差异其实很小很多人纠结用reduce还是for...of。从 V8 引擎的优化角度看两种写法编译后的性能差距在大多数场景下可以忽略。我自己的习惯是逻辑简单用reduce逻辑里有条件分支、需要提前 break 就换成for...of。分组场景通常会伴随后续统计逻辑所以我个人更偏爱for...of调试时可以打断点加判断也更自然。真正影响性能的不是遍历方式而是 Map 的 key 计算方式和桶的扩展。如果你在循环里拼接模板字符串做 key每次拼接都会创建新字符串几十万次循环下这也是不可忽略的开销。优化手段很简单把 key 计算过程提前到循环外面或者用整数编号代替字符串拼接。4.2 分组后的排序问题Map 保持插入序普通对象做分组容器时整数字符串 key 会按照数值大小自动升序排列。比如你的分组属性值是2、1、10遍历时你会惊讶地发现顺序是1, 2, 10而不是你插入时候的顺序而且字符串和数字混合时规律更难捉摸。Map没有这个问题它严格按照第一次set的顺序保存 key。如果需要自定义分组内的排序可以在 push 之后统一 sortconst grouped groupBy(input, item item.dept); for (const list of grouped.values()) { list.sort((a, b) b.score - a.score); // 组内按分数降序 }注意list是同一个数组的引用直接排序不会影响其他分组因为每个分组的数组都是独立的。4.3 超大数组的内存策略如果你只是做一个前端页面几千条数据随便折腾。但如果你在 Node.js 环境跑一个数据清洗脚本几十万条数据分组时就要考虑内存了。我有几个朴素但有效的建议如果分组后只关心统计值不关心原始记录就别把所有条目都塞进数组里直接在循环里累加统计字段这样内存占用从 O(n) 下降到 O(分组数)。如果原始数组本身不再需要可以把arr置空或者分块处理避免引用一直存在导致垃圾回收不了。如果只是枚举字段分组比如状态值只有 3 种可以用固定数组存桶然后用switch或者预定义的 map 来定位省去has/get的调用开销。我之前处理日志数据时就是把每条日志的原始对象做浅拷贝后放进桶里跑完之后原始数组和分组数组里全是引用关系内存直接翻倍。后来改成直接复用原始对象统计字段单独累加内存很快降下来了。要不要拷贝对象取决于后续是否有人会修改原数组里的数据这个我放在第 6 节讲坑的时候再展开。4.4 要不要上 lodash 的 groupBy如果你想用第三方库lodash 的_.groupBy很成熟底层实现跟我们的思路基本一样。团队里如果已经引入了 lodash直接用没毛病。如果只是为了分组这一个功能引入整个库那我会劝你手写。这样代码更透明也方便按业务需求定制 keySelector。而且_.groupBy返回的还是普通对象同样有原型链隐患用起来反而不如自己写的 Map 版本安心。5. 真实案例一周考勤记录按日期和部门双重分组前面讲的是函数和原理这一节我用一个完整的场景把它们串起来。假设我们需要处理某内部系统的考勤导出数据接口返回一周内所有员工的打卡记录每条记录包含日期、员工工号、部门、工作时长。需求是生成一个按“日期 → 部门 → 员工维度统计”的嵌套结构方便前端表格渲染。模拟数据如下const records [ { date: 2025-01-06, empId: E001, name: 小李, dept: 研发部, hours: 8 }, { date: 2025-01-06, empId: E002, name: 小张, dept: 研发部, hours: 8.5 }, { date: 2025-01-06, empId: E003, name: 小王, dept: 市场部, hours: 7 }, { date: 2025-01-07, empId: E001, name: 小李, dept: 研发部, hours: 7.5 }, { date: 2025-01-07, empId: E003, name: 小王, dept: 市场部, hours: 8 }, ];第一步按日期和部门生成两级嵌套 Mapfunction buildAttendanceTree(records) { const dateMap new Map(); for (const record of records) { if (!dateMap.has(record.date)) { dateMap.set(record.date, new Map()); } const deptMap dateMap.get(record.date); if (!deptMap.has(record.dept)) { deptMap.set(record.dept, []); } deptMap.get(record.dept).push(record); } return dateMap; } const tree buildAttendanceTree(records);第二步把嵌套 Map 转成前端渲染友好的数组结构同时去重同名员工并按工号聚合工时const result []; for (const [date, deptMap] of tree) { const dateGroup { date, depts: [] }; for (const [dept, list] of deptMap) { const statsByEmp new Map(); for (const item of list) { const cur statsByEmp.get(item.empId) ?? { empId: item.empId, name: item.name, totalHours: 0, days: 0, }; cur.totalHours item.hours; cur.days 1; statsByEmp.set(item.empId, cur); } dateGroup.depts.push({ dept, employees: [...statsByEmp.values()], }); } result.push(dateGroup); } console.log(JSON.stringify(result, null, 2));最终输出大概是[ { date: 2025-01-06, depts: [ { dept: 研发部, employees: [ { empId: E001, name: 小李, totalHours: 8, days: 1 }, { empId: E002, name: 小张, totalHours: 8.5, days: 1 } ] }, { dept: 市场部, employees: [ { empId: E003, name: 小王, totalHours: 7, days: 1 } ] } ] } ]这个例子里嵌套 Map 的价值在哪如果你用普通对象实现第一层日期还好第二层部门的 key 万一带着奇怪字符就会踩原型污染的雷。而且中间层用 Map 可以非常自然地“按需初始化”——每个日期第一次出现时才创建内层 Map无需预先声明所有部门结构。如果你要按季度分组只需要把第一层的 key 从record.date换成getQuarterOfDate(record.date)就完成了改动因为整个树的构建逻辑是通用的。6. 我踩过的几个分组坑现象、原因、修法6.1 日期格式不统一看起来一样其实是两个组有一次做报表用户反馈同一天的数据被拆成了两个分组。查了半天发现接口在传输过程中一部分记录的日期字段是2025-01-06另一部分是2025/01/06还有几条的日期字段带上了时间戳2025-01-06 00:00:00。字符串比较完全不相等分组自然分成了三个。从那以后我养成了一个习惯任何分组操作之前先对 key 做归一化。日期统一格式化到天级别数字统一转成字符串或数字布尔值显式转成true/false。归一化代码不要藏在业务代码里直接放进 keySelector 开头const normalizeDate (d) { if (d instanceof Date) return d.toISOString().slice(0, 10); return String(d).slice(0, 10); // 把 2025/01/06 00:00:00 统一成 2025-01-06 }; const byDate groupBy(records, item normalizeDate(item.date));6.2 对象引用共享改了分组里的对象原始数组也被改了JavaScript 对象赋值是引用传递分组数组里存的并不是复制品而是原对象的引用。之前有个同事在按状态分组展示后前端对某个分组里的对象做了字段修改结果原始数组里对应数据也静默变化了。这种 bug 不会报错但排查起来非常痛苦。如果你的分组结果会被外部修改或者你希望分组后的数据是独立快照push 的时候做一次浅拷贝map.get(key).push({ ...item }); // 或者更深层的 structuredClone(item)如果嵌套层级较多浅拷贝要注意嵌套对象还是共享的deep clone 成本高所以我的原则是优先明确“谁拥有数据”。分组只做只读展示就不拷贝分组结果还要交给表单编辑就拷贝一层再深层的拷贝只能说明数据设计有问题。6.3 reduce 初始值里的坑所有分组指向同一个数组这个坑很隐蔽代码看起来完全正常但结果就是每个 key 对应的数组内容全都一模一样。原因是初始值里放了一个共享数组引用const sharedList []; // 错误示范所有分组用的都是同一个 sharedList const buggy input.reduce((acc, item) { const key item.dept; if (!acc[key]) { acc[key] sharedList; } acc[key].push(item); return acc; }, {});第一次给研发部赋值时把sharedList塞进去第二次给市场部赋值时又把同一个sharedList塞进去。最终两个 key 操作的是同一个数组数据全都串了。正解是每次初始化时创建新数组acc[key] []。这也是我一直偏爱 Map 的原因我会明确写成map.set(key, [])代码一眼看到底不会出现共享引用的空间。6.4 分组结果的顺序依赖插入顺序普通对象还有个麻烦如果 key 是纯数字字符串比如员工工号1001、1002遍历时对象会按数字升序输出而不是按你插入的顺序。想要保持原始出现的顺序有两个方案一是用 Map天然有序二是分组前先给数组加一个序号字段最后按序号排序。我实际项目中改过很多次都是因为测试觉得“顺序不对”所以如果你一开始就面对新项目直接用 Map 版本不要为了省事用普通对象。6.5 空值和 undefined 的 key要不要给它们单独建组后端数据经常有些记录字段是 null、空字符串或者 undefined。if (!map.has(key))会把这些全都当成合法 key并建一个特殊分组。有时候产品希望忽略这些无效数据有时候又希望给它们一个“未分类”的落点。这类需求我建议在 keySelector 里显式处理const byDept groupBy(input, item item.dept || 未分配部门);这样空的、缺失的、为 null 的全部落到一个可读的分组里比默认建一个undefined分组要清晰得多。最后说点我的实操体会分组这个操作写法上几十行代码就搞定但真正决定代码质量的是几个关键决策输出结构选 Map 还是对象、key 是否做归一化、是否拷贝对象、是否做了特殊值处理。我在实际项目里踩过一轮坑之后至少养成了三个习惯分组容器一律用 MapkeySelector 抽成独立函数分组前花几十秒检查数据里的特殊值。这个检查不是为了预防万分之一的情况而是因为后端数据永远不会像 Prototype 文档里那样规整。按照这套思路写出来的分组代码基本就告别了原型污染、日期错分、引用共享这些阴魂不散的 bug。你可以把这篇文章里的groupBy函数直接复制到项目里试一下看看是不是比手写的 for 循环省心得多。

相关新闻

CefSharp Cookie持久化实战:存储、读取与多账号隔离

CefSharp Cookie持久化实战:存储、读取与多账号隔离

接手过几个需要内嵌浏览器的桌面项目之后,我对“CefSharp存储Cookie和读Cookie”这件事算是从踩坑踩到了门清。很多刚接触CefSharp的朋友会以为,Cookie存取无非就是调一个API的事,但实际上这套基于Chromium内核的封装,在会话持久化…

2026/10/19 4:07:34 阅读更多 →
Linux文件同步利器rsync:从原理到实战的完整指南

Linux文件同步利器rsync:从原理到实战的完整指南

提到 Linux 下的文件同步工具,rsync 是绕不开的一个名字。工作这些年,从最早的本地备份脚本,到后来给服务器搭建异地容灾,再到给多台机器做配置分发,几乎离不开它。很多刚接触 Linux 的朋友会觉得 rsync 命令参数又多又…

2026/10/19 4:07:34 阅读更多 →
B站视频下载爬虫:requests取流+进度条+ffmpeg合并实战

B站视频下载爬虫:requests取流+进度条+ffmpeg合并实战

简介:针对批量下载哔哩哔哩小视频的实用爬虫脚本,面向Python入门至进阶的爬虫学习者,解决视频批量采集与下载进度可视化问题。程序按循环遍历10页排行榜JSON信息,自动提取标题与视频地址,通过requests模块逐一下载&…

2026/10/19 4:07:34 阅读更多 →

最新新闻

CentOS下BIND域名解析配置实战:从正向/反向解析到故障排查

CentOS下BIND域名解析配置实战:从正向/反向解析到故障排查

1. 项目概述:CentOS下的域名解析配置到底要做什么第五个年头做Linux运维,最常见的需求之一就是“帮我把域名解析配置上”。这句话在上线部署、内网服务联调、测试环境搭建里反复出现。尤其是新买了一台CentOS服务器,业务程序部署完了&#xf…

2026/10/19 4:48:02 阅读更多 →
select与poll深度剖析:从原理到实战,理解I/O多路复用核心机制

select与poll深度剖析:从原理到实战,理解I/O多路复用核心机制

早几年我刚入行做服务端开发的时候,接手了一个老项目,核心网络模块就是拿select写的。那时候我并没有多想,只觉得它把我从"一个连接一个线程"的泥潭里捞了出来,代码也能跑,线上也没啥大问题。直到后来自己写…

2026/10/19 4:48:02 阅读更多 →
Kafka集群部署实战:从单节点到高可用配置全解析

Kafka集群部署实战:从单节点到高可用配置全解析

1. 部署前的思维准备:Kafka 到底是怎么跑起来的很多人在第一次接触 Kafka 安装部署时,习惯直接上网搜一条命令复制粘贴,结果不是版本对不上,就是启动报一堆看不懂的异常。我在最开始踩坑时也这样,后来花了很长时间把 K…

2026/10/19 4:48:02 阅读更多 →
open-code-review实践:构建高效透明的代码审查流程

open-code-review实践:构建高效透明的代码审查流程

1. open-code-review 的命名背后:它到底在表达什么理念我第一次看到 open-code-review 这个项目名时,第一反应是:这不就是把 code review 加了个 open 前缀么?但真把这几个词拆开琢磨一遍,会发现事情没那么简单。它在工…

2026/10/19 4:48:02 阅读更多 →
图像缩放插值算法选型:从最近邻到边缘定向,MATLAB实现与性能对比

图像缩放插值算法选型:从最近邻到边缘定向,MATLAB实现与性能对比

简介:这份毕业论文以数字图像缩放技术为研究主题,面向图像处理学习者与电子信息类本科毕业生,系统介绍了数字图像采集、存储、处理与分析的基本流程,并重点剖析最近邻插值、双线性插值、双三次插值等经典算法,通过MATL…

2026/10/19 4:48:02 阅读更多 →
代码审查自动化实践:open-code-review规则引擎与CI集成详解

代码审查自动化实践:open-code-review规则引擎与CI集成详解

1. 为什么我要折腾一个叫 open-code-review 的东西先交代一下背景。我一直在维护一个十几人的研发团队,代码审查这件事从我开始带队伍起就断断续续做过很多轮,但说实话效果一直不稳定。团队的 Code Review 流程一开始用的是最原始的方式:提 P…

2026/10/19 4:47:01 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →