- 开发工具
【免费下载链接】collect.js
💎 Convenient and dependency free wrapper for working with arrays and objects
avg()是 collect.js(一个零依赖、专门用于操作数组与对象的便捷封装库)中用于计算集合内所有元素平均值的核心方法。本文以官方文档 docs/api/avg.md 为主线,结合 src/methods/average.js 的源码实现与 test/methods/avg_test.js 测试用例,系统讲解avg()的无参求值、按 key 取值、回调函数三种调用模式,并深入剖析其底层对sum()与pluck()的调用链,帮助你准确、高效地在实际项目中使用这一方法。
一、基本用法:无参数计算平均值
当集合中的每一项本身就是可参与数值运算的原始值时,直接调用avg()即可得到全部元素的算术平均值。官方文档给出的示例是:
collect([1, 3, 3, 7]).avg(); // 3.5计算过程即(1 + 3 + 3 + 7) / 4 = 14 / 4 = 3.5。这一场景适用于纯数字数组,也适用于内容为数字字符串的数组——因为底层求和实现会通过parseFloat对每个元素做数值转换(详见下文源码剖析)。
需要说明的是,avg()对对象集合同样有效:当集合是普通对象(而非数组)时,其内部会先取出对象的全部属性值再参与运算,这是由 src/helpers/values.js 辅助函数决定的。
二、指定 key:对嵌套数组 / 对象按字段求平均值
当集合中的元素是嵌套数组或对象时,你需要传入一个key,avg()会按该 key 提取出每个元素对应的值,再计算这些值的平均值。文档示例:
const collection = collect([ { name: 'My story', pages: 176, }, { name: 'Fantastic Beasts and Where to Find Them', pages: 1096, }, ]); collection.avg('pages'); // 636验证计算:(176 + 1096) / 2 = 1272 / 2 = 636,结果与文档一致。
从源码结构看,key 模式并非简单地在average()中直接索引,而是先借助pluck(key)提取出所有目标值,再交给sum()求和。而 src/methods/pluck.js 内部通过 src/helpers/nestedValue.js 支持点号分隔的嵌套路径(如'author.age'、'metadata.pageCount'),因此可以推断:avg()的 key 参数同样支持访问多级嵌套字段,例如:
collect([ { meta: { score: 80 } }, { meta: { score: 100 } }, ]).avg('meta.score'); // 90这是一个文档未展开、但由源码可直接推导出的实用能力。
三、回调函数模式:自定义取值逻辑
除了 key 字符串,avg()还接受一个回调函数,回调接收集合中的每个元素并返回用于参与求平均的数值。官方文档示例:
const collection = collect([ { name: 'My story', pages: 176, }, { name: 'Fantastic Beasts and Where to Find Them', pages: 1096, }, ]); collection.avg(book => book.pages); // 636回调模式与 key 模式的计算结果一致(都是对pages字段求平均),但灵活性更高:你可以对元素做任意转换后再求和,例如collection.avg(item => item.pages * 2)、collection.avg(item => Number(item.price))等。回调与 key 两种模式可以按"语义清晰度 vs 计算灵活性"取舍选用。
四、average()别名:两条路径指向同一实现
在 collect.js 中,avg()并非独立实现,而是average()的别名。这一点在仓库中有两处直接证据:
src/methods/avg.js 的完整内容只有三行,直接 re-export 了 average 模块:
'use strict'; const average = require('./average'); module.exports = average;在 src/index.js 中,两个方法被依次挂载到
Collection.prototype上:Collection.prototype.average = require('./methods/average'); Collection.prototype.avg = require('./methods/avg');
因此collection.average()与collection.avg()在行为上完全等价,官方文档 docs/api/average.md 也明确标注其为avg()的别名。你可以按项目习惯任选其一,两者均被 test/methods/average_test.js 覆盖验证。
五、源码剖析:average()的三分支实现
真正承载全部逻辑的是 src/methods/average.js,其实现非常精简:
'use strict'; const { isFunction } = require('../helpers/is'); module.exports = function average(key) { if (key === undefined) { return this.sum() / this.items.length; } if (isFunction(key)) { return new this.constructor(this.items).sum(key) / this.items.length; } return new this.constructor(this.items).pluck(key).sum() / this.items.length; };整个方法围绕key参数的类型拆分为三个分支:
| 分支条件 | 计算路径 | 适用场景 |
|---|---|---|
key === undefined | this.sum() / items.length | 纯数组 / 对象值集合,直接求均值 |
isFunction(key)为真 | sum(key) / items.length | 需要自定义取值逻辑时 |
| 其余情况(字符串 key) | pluck(key).sum() / items.length | 按对象字段或嵌套路径取值时 |
关键细节如下:
1. 分子统一来自sum(),分母统一来自this.items.length。三种模式最终都归结为"总和 ÷ 元素个数"。需要注意:函数与 key 分支会通过new this.constructor(this.items)重建一个等价集合再调用sum/pluck,从而保证调用链上的sum()与pluck()都有独立的、可复用的集合上下文,不会污染原集合——test/methods/avg_test.js 中expect(collection.all()).to.eql(products)正是对这一"原集合不被修改"特性的回归验证。
2. 求和内部使用parseFloat做数值化。src/methods/sum.js 对每个元素执行total += parseFloat(items[i])(函数模式为parseFloat(key(items[i])),key 模式为parseFloat(items[i][key])),因此字符串形式的数字(如'3.5')也能正确参与求和;求和完成后还会执行parseFloat(total.toPrecision(12))对结果做精度规整,这保证了avg()在大多数业务数据上能得到稳定、可预期的输出。
3. key 模式依赖pluck()的取值能力。如第二部分所述,pluck()借助nestedValue支持点号嵌套路径,并在取不到值时返回null。这意味着:若某条记录的字段缺失,sum()会对null执行parseFloat(null)(结果为NaN),进而使该次avg()返回NaN。因此在使用 key 模式前,建议先确认目标字段在集合中的完整性。
六、TypeScript 类型声明
如果你在 TypeScript 项目中使用 collect.js,仓库根目录的 index.d.ts 为该方法提供了完整类型定义:
/** * Alias for the avg() method. */ average<K>(key?: keyof Item | K): number; /** * The avg method returns the average of all items in the collection. */ avg<K>(key?: keyof Item | K): number;可以看出:
key是可选参数,缺省即"无参求平均"模式;- 传入时可使用集合元素自身的 key(
keyof Item)或任意泛型 K(对应回调/嵌套路径等场景); - 返回值恒为
number。
七、测试验证:文档示例的自动化保障
仓库通过 test/methods/avg_test.js 与 test/methods/average_test.js 覆盖了avg()的三种核心行为:
// avg_test.js it('should return the average value of collection values', () => { expect(collect([1, 3, 3, 7]).avg()).to.eql(3.5); }); it('avg is an alias for the average method', () => { expect(collect([1, 3, 3, 7]).avg()).to.eql(3.5); expect(collect([1, 3, 3, 7]).unique().avg()).to.eql(3.6666666666666665); }); it('should return the average value of collection values by key', () => { const collection = collect(products); const avg = collection.avg('price'); expect(avg).to.eql(550); expect(collection.all()).to.eql(products); });这些用例直接印证了文档中的三件事:[1, 3, 3, 7]的平均值为3.5;avg()与average()行为一致;按 key 求平均(products的price字段,(600 + 900 + 150) / 3 = 550)的同时原集合保持不变。运行测试可执行(当前仓库 package.json 中约定的命令):
npm test其会先通过 Babel 将src转译到dist,再以 mocha 运行 test/tests.js 聚合的全部方法测试。
八、使用注意事项与边界情况
结合源码,使用avg()时建议留意以下几点:
- 空集合会得到
NaN:当this.items.length为 0 时,sum()为 0,0 / 0在 JavaScript 中结果为NaN。从源码结构看,average()未对空集合做特殊保护,因此对可能为空的集合应自行判空后再调用。 - 字段缺失 / 非数值内容会导致
NaN:key 模式与回调模式的求和依赖parseFloat,null、undefined、无法解析为数字的字符串都会让该元素贡献NaN,进而污染最终平均值。 - 浮点精度的客观限制:平均值由"规整后的总和 ÷ 数量"得出,某些除不尽的结果会保留 IEEE 754 浮点表示(如测试中的
3.6666666666666665)。若对精度有严格要求的场景,建议结合toPrecision()或toFixed()自行做展示层规整。
小结
avg()是 collect.js 中"简洁 API 背后隐藏完整调用链"的典型代表:文档只展示了三种调用形态,而 src/methods/average.js 用不到 15 行代码将其统一为"sum() / items.length"这一个计算模型,并分别复用了sum()的parseFloat数值化能力与pluck()的嵌套路径取值能力。无论你是处理纯数组、对象数组还是需要自定义取值的场景,都可以放心地在上述三种模式中选择最合适的一种,并借助测试用例与类型声明获得一致的运行保障。
- 开发工具
【免费下载链接】collect.js
💎 Convenient and dependency free wrapper for working with arrays and objects
相关推荐
collect.js 的 average() / avg() 方法:三种平均值计算方式与源码级原理解析
collect.js 的 average / avg 方法:三种平均值计算方式与源码级原理解析 average 是 collect.js 集合库中用于计算集合内
开发工具dash.js 吞吐量计算模式(Throughput Calculation)完全指南:八种均值算法、配置参数与源码剖析
dash.js 吞吐量计算模式(Throughput Calculation)完全指南:八种均值算法、配置参数与源码剖析 dash.js 提供了多种可配置的"当
音视频前端Sails 中 Waterline 模型的 `.avg()` 聚合方法:跨记录计算数值属性的算术平均值
Sails 中 Waterline 模型的 .avg 聚合方法:跨记录计算数值属性的算术平均值 导读 .avg 是 Sails 内置 ORM——Waterlin
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考