如何使用Ramda.js的transducers直接计算符合条件的timestamp平均值
Ramda.js Transducer 直接计算平均值的实现方案
问题原因
你之前的写法不生效的核心原因是:R.transduce 接收的第二个参数是 reducer 函数,必须符合 (累加器, 当前值) => 新累加器 的签名,而你定义的 average 是接收完整数组计算平均值的函数,不符合 reducer 的入参要求,因此无法直接使用。
实现思路
要在单次 transducer 迭代中直接完成平均值计算,只需要让累加器同时存储两个状态:
- 符合过滤条件的 timestamp 总和
- 符合过滤条件的条目数量
迭代结束后再将总和除以数量,即可得到目标平均值,全程不会生成中间数组,完全保留 transducer 的性能优势。
完整代码实现
const createCheckin = ({ timestamp = Date.now(), // default is now startStation = 'foo', endStation = 'bar' } = {}) => ({timestamp, startStation, endStation}); const checkins = [ createCheckin(), createCheckin({ startStation: 'baz' }), createCheckin({ timestamp: Date.now() + 100 }), // offset of 100 ]; const filterCheckins = R.filter(({ startStation }) => startStation === 'foo'); const mapTimestamps = R.map(({ timestamp }) => timestamp); const transducer = R.compose( filterCheckins, mapTimestamps, ); // 迭代用reducer:每次收到timestamp更新总和和计数 const averageReducer = (acc, timestamp) => ({ sum: acc.sum + timestamp, count: acc.count + 1 }) // 迭代完成后计算平均值 const calcFinalAverage = R.converge(R.divide, [R.prop('sum'), R.prop('count')]) // 组合调用 const result = calcFinalAverage( R.transduce(transducer, averageReducer, { sum: 0, count: 0 }, checkins) ) console.log(result) // 输出为 Date.now() + 50,符合预期
更贴合Ramda风格的写法
如果你想完全用Ramda的函数式风格定义reducer,可以写成:
const averageReducer = R.useWith(R.mergeRight, [ R.identity, R.applySpec({ sum: R.add(R.__, R.prop('sum')), count: R.pipe(R.prop('count'), R.inc) }) ])
内容的提问来源于stack exchange,提问作者J. Hesters
相关产品推荐
相关产品推荐

