繁体   English   中英

GroupBy并以无点样式减少对象数组

[英]GroupBy and reduce an array of object in a pointfree style

我最近开始使用Ramda ,并试图找到一个pointfree的方式来写,以减少对象的数组的方法。

这是对象数组:

const someObj = [
    {
        name: 'A',
        city: 1,
        other: {
            playtime: 30
        }
    },
    {
        name: 'B',
        city: 2,
        other: {
            playtime: 20
        }
    },
    {
        name: 'c',
        city: 1,
        other: {
            playtime: 20
        }
    }
];

我正在尝试的是使用无条件样式的ramda来减少对象

{
    '1': {
        count: 2,
        avg_play_time: 20 + 30 / count
    },
    '2': {
        count: 1,
        avg_play_time: 20 / count
    }
}

我可以使用数组reduce方法来做,但不知道如何以ramda pointfree样式编写相同的方法。 任何建议将不胜感激。

一种解决方案是做这样的事情:

// An optic to extract the nested playtime value
// Coupled with a `lift` operation which allows it to be applied over a collection
// Effectively A -> B => A[] -> B[]
const playtimes = R.lift(R.path(['other', 'playtime']))

R.pipe(
  // Group the provided array by the city value
  R.groupBy(R.prop('city')),
  // Return a body specification which computes each property based on the 
  // provided function value.
  R.map(R.applySpec({
    count: R.length,
    average: R.pipe(playtimes, R.mean)
  }))
)(someObj)

R.reduceBy还有另一个名为R.reduceBy函数,它在reducegroupBy提供了一些东西,允许你将匹配键的值折叠起来。

因此,您可以创建一个如下所示的数据类型,用于计算要平均的值。

const Avg = (count, val) => ({ count, val })
Avg.of = val => Avg(1, val)
Avg.concat = (a, b) => Avg(a.count + b.count, a.val + b.val)
Avg.getAverage = ({ count, val }) => val / count
Avg.empty = Avg(0, 0)

然后使用R.reduceBy将它们组合在一起。

const avgCities = R.reduceBy(
  (avg, a) => Avg.concat(avg, Avg.of(a.other.playtime)),
  Avg.empty,
  x => x.city
)

然后拉平均值出的Avg成最终对象的形状。

const buildAvg = R.applySpec({
  count: x => x.count,
  avg_play_time: Avg.getAverage
})

最后将两者连接在一起,将buildAvg映射到对象中的值。

const fn = R.pipe(avgCities, R.map(buildAvg))
fn(someObj)

这是另一个建议,使用reduceBy在结果对象的每个属性上映射applySpec函数:

我们的想法是使用getPlaytimeByCitysomeObj转换为此对象:

{ 1: [30, 20],
  2: [20]}

然后,您可以在该对象的每个属性上映射stats函数:

stats({ 1: [30, 20], 2: [20]});
// { 1: {count: 2, avg_play_time: 25}, 
//   2: {count: 1, avg_play_time: 20}}

 const someObj = [ { name: 'A', city: 1, other: { playtime: 30 }}, { name: 'B', city: 2, other: { playtime: 20 }}, { name: 'c', city: 1, other: { playtime: 20 }} ]; const city = prop('city'); const playtime = path(['other', 'playtime']); const stats = applySpec({count: length, avg_play_time: mean}); const collectPlaytime = useWith(flip(append), [identity, playtime]); const getPlaytimeByCity = reduceBy(collectPlaytime, [], city); console.log( map(stats, getPlaytimeByCity(someObj)) ); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script> <script>const {prop, path, useWith, flip, append, identity, applySpec, length, mean, reduceBy, map} = R;</script> 

我会这样写,希望它有所帮助!

 const stats = R.pipe( R.groupBy(R.prop('city')), R.map( R.applySpec({ count: R.length, avg_play_time: R.pipe( R.map(R.path(['other', 'playtime'])), R.mean, ), }), ), ); const data = [ { name: 'A', city: 1, other: { playtime: 30 } }, { name: 'B', city: 2, other: { playtime: 20 } }, { name: 'c', city: 1, other: { playtime: 20 } }, ]; console.log('result', stats(data)); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script> 

我喜欢到目前为止给出的所有其他答案。 所以很自然地我想添加自己的。 ;-)

这是一个使用reduceBy来保持计数和平均值的运行轨迹的版本。 如果您正在寻找中值或其他统计数据,这将无效,但给定计数,平均值和新值,我们可以直接计算新计数和平均值。 这允许我们仅以每次迭代执行某些算术为代价迭代数据一次。

 const transform = reduceBy( ({count, avg_play_time}, {other: {playtime}}) => ({ count: count + 1, avg_play_time: (avg_play_time * count + playtime) / (count + 1) }), {count: 0, avg_play_time: 0}, prop('city') ) const someObj = [{city: 1, name: "A", other: {playtime: 30}}, {city: 2, name: "B", other: {playtime: 20}}, {city: 1, name: "c", other: {playtime: 20}}] console.log(transform(someObj)) 
 <script src="https://bundle.run/ramda@0.26.1"></script> <script> const {reduceBy, prop} = ramda </script> 

这不是免费的。 虽然我是无点风格的忠实粉丝,但我只在它适用的时候使用它。 我认为为它自己寻找它是一个错误。

请注意,可以很容易地修改Scott Christopher的答案以使用这种计算方法

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM