【问题标题】:Sum values of collection by type and assign to new property on replicated object按类型汇总集合的值并分配给复制对象的新属性
【发布时间】:2017-03-15 16:10:03
【问题描述】:

我正在尝试对集合中的值求和并将它们添加到每个对象的新属性中

var collection = [
 {prop:'title',quan:2},
 {prop:'body',quan:3},
 {prop:'title',quan:2},
 {prop:'title',quan:4},
]

/* desired result
 [
  {prop:'title', quan:2, stock:8},
  {prop:'body', quan:3, stock:3},
  {prop:'title', quan:2, stock:8},
  {prop:'title', quan:4, stock:8},
 ]
*/

我尝试了许多不同的方法,但都没有成功。我正在尝试以一种实用的方式来做到这一点。

这是我目前卡住了,我很确定这是最简洁的方式。

// group the props using key
var result = _.groupBy(collection,'prop');

哪个输出

{
  title:[
   {prop:'title',quan:2},{prop:'title',quan:2},{prop:'title',quan:4}
  ], 
  body:[
   {prop:'body':quan:3}
  ]
}

所以让我们减少我们创建的数组

var obj = {};
_.forEach(result,function(value,key){    
    obj[key] = _.reduce(value,function(acc,val){
      return acc.quan + val.quan
    });       
});

上面的这部分不起作用?

当我完成这项工作时,我应该能够将其映射回我的最终收藏。

我们知道总数,将它们映射到集合

var final = _.map(collection,function(value){
  return {
    type:value.prop,
    val:value.quan,
    stock:obj[value.prop]
  }
});

jsbin

【问题讨论】:

  • “我正在尝试以一种实用的方式来做这件事。” – 那么你不应该改变你的输入,fwiw
  • @noamik 我已经阅读了一些资料,因为您提到数据不应该被变异并且现在对函数式编程有了更好的理解,尽管您的回答仍然有点超出我的理解。

标签: javascript functional-programming


【解决方案1】:

首先您必须获取 sum 对象,然后将 sum 分配给相应的对象。像这样:

function stock(arr) {
  // using lodach: var sum = _.reduce(arr, ...)
  var sum = arr.reduce(function(s, o) {     // get the sum object (hashing)
    s[o.prop] = (s[o.prop] || 0) + o.quan;  // if the sum already contains an entry for this object porp then add it to its quan otherwise add 0
    return s;
  }, {});
  
  // using lodash: _.forEach(arr, ...);
  arr.forEach(function(o) {                 // assign the sum to the objects
    o.stock = sum[o.prop];                  // the stock for this object is the hash of it's prop from the sum object
  });
}

var collection = [
 {prop:'title',quan:2},
 {prop:'body',quan:3},
 {prop:'title',quan:2},
 {prop:'title',quan:4},
]

stock(collection);
console.log(collection);

如果您想返回一个新数组并保持原始数组不变,请像您已经使用的那样使用map 而不是像这样的forEach

// using lodash: _.map(arr, ...);
return arr.map(function(o) {                // create new objects with stock property
  return {
    prop: o.prop,
    quan: o.quan,
    stock: sum[o.prop]
  };
  // or in ecmascript: return Object.assign({}, o, {stock: sum[o.prop]});
});

然后你必须像这样使用它:

var newArray = stock(collection);

【讨论】:

  • 前段时间我很接近那个,但是非常偏离,谢谢。
  • 不错的答案!我想不出任何更简单的方法来做到这一点
  • @ibrahimmahrir 啊,我现在明白了。对于那个很抱歉。我将删除我之前的评论以避免混淆。
【解决方案2】:

我更喜欢这个解决方案,因为它抽象了排序规则,但允许您使用高阶函数控制项目的排序方式。

请注意,我们在 collat​​eBy 函数中根本不讨论数据的种类或结构——这使我们的函数保持通用并允许它处理任何形状的数据。

collateBy 采用分组函数 f 和归约函数 g 以及任何类型数据的同构数组

// collateBy :: (a -> b) -> ((c,a) -> c) -> [a] -> Map(b:c)
const collateBy = f => g => xs => {
  return xs.reduce((m,x) => {
    let v = f(x)
    return m.set(v, g(m.get(v), x))
  }, new Map())
}

const collateByProp = collateBy (x => x.prop)

const assignTotalStock = xs => {
  let stocks = collateByProp ((acc=0, {quan}) => acc + quan) (xs)
  return xs.map(({prop, quan}) =>
    ({prop, quan, stock: stocks.get(prop)}))
}

var collection = [
 {prop:'title',quan:2},
 {prop:'body',quan:3},
 {prop:'title',quan:2},
 {prop:'title',quan:4},
]

console.log(assignTotalStock(collection))
// [ { prop: 'title', quan: 2, stock: 8 },
//   { prop: 'body', quan: 3, stock: 3 },
//   { prop: 'title', quan: 2, stock: 8 },
//   { prop: 'title', quan: 4, stock: 8 } ]
   

在处理数据时执行排序规则非常常见,因此在每个需要它的函数中编码排序规则行为是没有意义的。取而代之的是,使用仅捕获排序规则计算本质的通用高阶函数,并使用分组函数f 和归约函数g 对其进行专门化

【讨论】:

    猜你喜欢
    • 2020-05-22
    • 2020-08-11
    • 1970-01-01
    • 2016-01-03
    • 1970-01-01
    • 2021-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多