collect.js 的 average() / avg() 方法:三种平均值计算方式与源码级原理解析
开发工具【免费下载链接】collect.js Convenient and dependency free wrapper for working with arrays and objects项目地址https://gitcode.com/gh_mirrors/co/collect.js点击查看免费下载average()是 collect.js 集合库中用于计算集合内所有元素平均值的便捷方法它与avg()完全等价avg()是实际实现、average()是别名。本文围绕 average() 官方文档 与配套的 avg() 文档结合 average.js 源码、底层 sum.js 与 pluck.js 实现以及 average_test.js、avg_test.js 测试用例完整讲解无参求和、按键取值、回调函数三种调用形态并深入其背后的求和与取值原理、边界行为与 TypeScript 类型签名读完即可在真实项目中正确、稳健地使用该 API。方法定位average() 是 avg() 的别名average()文档本身只有一句话定义它是avg()方法的别名Alias for theavg()method。这一点在源码中有三重印证实现文件直接复用src/methods/avg.js 全文仅一条语句直接require(./average)导出说明avg与average指向同一个函数对象use strict; const average require(./average); module.exports average;注册入口同时挂载src/index.js 中Collection.prototype.average与Collection.prototype.avg分别加载两个方法文件因此无论调用哪一个行为完全一致。测试用例明确验证别名关系avg_test.js 断言collect([1, 3, 3, 7]).avg()与collect([1, 3, 3, 7]).average()结果相同。所以下面关于avg()的每一种用法与行为对average()同样成立文章中不再重复区分。基本用法无参数计算所有元素的平均值当集合中的元素本身就是数值时直接调用avg()即可无需任何参数collect([1, 3, 3, 7]).avg(); // 3.5对应的average()写法collect([1, 3, 3, 7]).average(); // 3.5从 average.js 源码 看无参分支的实现是module.exports function average(key) { if (key undefined) { return this.sum() / this.items.length; } // ... };即先调用集合自身的sum()求出元素总和再除以元素个数this.items.length得到算术平均值。这里的sum()实现位于 src/methods/sum.js它对每个元素执行parseFloat(items[i])并累加因此数字字符串如3也会被解析为数值参与运算。按键key计算嵌套对象属性的平均值当集合元素是对象或嵌套数组时需要传入一个key指明用哪个属性的值参与求平均const collection collect([ { name: My story, pages: 176, }, { name: Fantastic Beasts and Where to Find Them, pages: 1096, }, ]); collection.avg(pages); // 636对应average(pages)结果相同即(176 1096) / 2 636。该分支的源码实现为 average.jsreturn new this.constructor(this.items).pluck(key).sum() / this.items.length;它通过new this.constructor(this.items)基于原集合元素重建一个新集合不会修改原集合这一点由 average_test.js 断言collection.all()仍等于原products得到验证然后调用pluck(key)抽出对应属性的值再求和、除以原集合长度。得益于 pluck.js 内部使用 nestedValue.js 解析属性路径后者支持用.分隔的多级路径例如key.split(.).reduce(...)avg()的key参数同样支持嵌套路径写法例如collect([ { order: { amount: 100 } }, { order: { amount: 300 } }, ]).avg(order.amount); // 200使用回调函数自定义取值逻辑当平均值的计算逻辑不能用一个简单属性名表达时例如需要对属性做变换、过滤后取值或取值来自多个属性组合可以传入一个回调函数collect.js 会以每个元素为参数调用它const collection collect([ { name: My story, pages: 176, }, { name: Fantastic Beasts and Where to Find Them, pages: 1096, }, ]); collection.avg(book book.pages); // 636回调分支的源码为 average.jsif (isFunction(key)) { return new this.constructor(this.items).sum(key) / this.items.length; }其中isFunction来自 src/helpers/is.js本质是typeof item function。传入的sum(key)会对每个元素执行parseFloat(key(items[i]))后累加所以回调的返回值既可以是数值也可以是可被parseFloat解析的字符串。回调形态是三种调用方式中扩展性最强的你可以在回调里做item.pages * 2、拼接多个字段、甚至先过滤再映射因为sum()只会消费回调的返回值。源码级原理average() 的三分支结构与底层依赖把 average.js 完整展开其逻辑清晰分为三条路径调用形态条件求值方式无参key undefinedthis.sum() / this.items.length回调函数isFunction(key)new this.constructor(this.items).sum(key) / this.items.length属性名其他情况new this.constructor(this.items).pluck(key).sum() / this.items.length三个分支共享两个关键设计复用而非重写average不自己遍历元素而是委托给集合自身的sum()与pluck()方法。这种组合式实现让平均值计算天然继承了sum()的精度处理和pluck()的嵌套取值能力代码量极小且行为统一。不变性immutability带参分支通过new this.constructor(this.items)基于原元素重建集合再求值原集合数据不会被改写无参分支直接读取this.items也仅做读取。关于浮点精度值得留意sum()的收尾处理——sum.js 返回parseFloat(total.toPrecision(12))即把累加结果先按 12 位有效数字四舍五入再转回数字。这一处理能削减大部分累加误差但除法引入的小数位仍可能超出预期例如测试用例中的collect([1, 3, 3, 7]).unique().avg()得到3.6666666666666665而非精确的3.666...6这是 IEEE 754 浮点数的正常表现需要精确结果时应自行toFixed处理。测试验证三种形态均有对应用例仓库测试 test/methods/average_test.js 覆盖了average()的全部三种调用形态可直接作为行为规范参考it(should return the average value of collection values, () { expect(collect([1, 3, 3, 7]).average()).to.eql(3.5); }); it(should accept a callback function, () { const avg collect(products).average(product product.price); expect(avg).to.eql(550); }); it(should return the average value of collection values by key, () { const collection collect(products); const avg collection.average(price); expect(avg).to.eql(550); expect(collection.all()).to.eql(products); });测试数据products [{ name: Chair, price: 600 }, { name: Desk, price: 900 }, { name: Lamp, price: 150 }]的均价为(600 900 150) / 3 550。而 avg_test.js 除同样覆盖按键、回调外还专门验证了别名等价性与链式组合unique().avg()证明avg/average可以与unique()等方法流畅串联。边界情况与注意事项结合源码可以确认以下几点行为供实战时规避坑点空集合无参分支中sum()对空集合返回0sum.js 循环不执行total保持 0再除以this.items.length0最终得到NaN。从 average.js 的实现结构可以推断对空集合调用average()/avg()不会抛错但会返回NaN业务上应先判空。混合类型元素sum()对所有元素统一执行parseFloat非数字内容会被解析为NaN并污染累加结果最终均价同样可能变成NaN因此参与求平均的元素应保证为数值或数字字符串。属性缺失按键分支中pluck()对缺失的属性会返回null见 pluck.js 的兜底逻辑null经parseFloat后为NaN同样会影响均价。不修改原集合带参分支使用新构造的集合完成求值average_test.js 对此有显式断言。TypeScript 类型签名collect.js 自带类型声明 index.d.tsaverage与avg的签名如下index.d.ts/** * Alias for the avg() method. */ averageK(key?: keyof Item | K): number; /** * The avg method returns the average of all items in the collection. */ avgK(key?: keyof Item | K): number;key可选类型为keyof Item | K既可以是集合元素的属性名也可以是任意泛型配合回调使用时由回调参数推导。返回值统一为number。需要指出的是该签名将回调形态也归类在可选key参数下实际传函数时 TypeScript 会要求你自行断言或使用泛型这是类型声明对三种调用形态的简化建模。典型链式场景average()/avg()通常与过滤、排序、去重等操作组合使用例如基于 usage.md 的链式风格collect(products) .where(price, , 299) // 先过滤 .avg(price); // 再求平均 collect([1, 3, 3, 7]) .unique() // 去重后元素为 [1, 3, 7] .avg(); // 3.6666666666666665作为 collect.js 核心 API 的一员average()与avg()完全免费依赖package 本身即为零依赖的数组/对象包装库在需要对一组数据进行集中趋势统计时是比手写reduce循环更简洁、可读性更高的选择。赞分享开发工具【免费下载链接】collect.js Convenient and dependency free wrapper for working with arrays and objects项目地址https://gitcode.com/gh_mirrors/co/collect.js点击查看免费下载相关推荐Sails 中 Waterline 模型的 .avg() 聚合方法跨记录计算数值属性的算术平均值Sails 中 Waterline 模型的 .avg 聚合方法跨记录计算数值属性的算术平均值 导读 .avg 是 Sails 内置 ORM——Waterlin后端RxGo Average 算子详解从 AverageInt 到并行平均值计算RxGo Average 算子详解从 AverageInt 到并行平均值计算 导读 本文聚焦 RxGoReactive Extensions for the后端RxJS 4 的 average 操作符聚合平均值计算的源码剖析与实战指南RxJS 4 的 average 操作符聚合平均值计算的源码剖析与实战指南 Rx.Observable.prototype.average 是 RxJS 4后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考