减少和分组对象数组

Reduce and Group an Array of Objects

这是我的对象数组。我正在 google 工作表中编写自定义脚本以获取交易数据并将其分解为唯一项目列表,其中将订购金额相加以创建更易于阅读的购买历史记录。

 products = [ 
      { id: 1,
        item: 'Beef',
        category: 'Meat',
        orderAmount: 5,
        caseSize: 1,
        unit: 'Lb',
        price: 50,
        supplier: 'Sysco' },
      { id: 2,
        item: 'Chicken',
        category: 'Meat',
        orderAmount: 10,
        caseSize: 2,
        unit: 'Grams',
        price: 100,
        supplier: 'Findlay' },
      { id: 3,
        item: 'Fish',
        category: 'Seafood',
        orderAmount: 15,
        caseSize: 3,
        unit: 'Lb',
        price: 40,
        supplier: 'Deodato' },
      { id: 1,    // This is an example duplicate entry
        item: 'Beef',
        category: undefined,
        orderAmount: 100,
        caseSize: 1,
        unit: 'Lb',
        price: 50,
        supplier: 'Sysco' } 
    ]

我对如何像这样将其分解为另一个对象有些困惑,其中删除了重复项,但将重复项的 orderAmounts 相加。

uniqueProducts = [ 
  { id: 1,
    item: 'Beef',
    category: 'Meat',
    orderAmount: 105, //this is the altered amount
    caseSize: 1,
    unit: 'Lb',
    price: 50,
    supplier: 'Sysco' },
  { id: 2,
    item: 'Chicken',
    category: 'Meat',
    orderAmount: 10,
    caseSize: 2,
    unit: 'Grams',
    price: 100,
    supplier: 'Findlay' },
  { id: 3,
    item: 'Fish',
    category: 'Seafood',
    orderAmount: 15,
    caseSize: 3,
    unit: 'Lb',
    price: 40,
    supplier: 'Deodato' }
]

我一直在阅读有关使用 map 和 reduce 函数的信息,但我很难弄清楚如何使用如此大的对象来实现它们。

您可以编写一个通用的 group 函数,该函数采用 grouper 函数来指定使项目重复的原因,以及 update 函数来指定如何更新结果发现重复项 -

function group(input, grouper, update) {
  function reducer(res, item) {
    const uniq = grouper(item)
    return res.has(uniq)
      ? res.set(uniq, update(res.get(uniq), item))
      : res.set(uniq, item)
  }
  return Array.from(input.reduce(reducer, new Map).values())
}

const products = 
  [{id:1,item:'Beef',category:'Meat',orderAmount:5,caseSize:1,unit:'Lb',price:50,supplier:'Sysco'},{id:2,item:'Chicken',category:'Meat',orderAmount:10,caseSize:2,unit:'Grams',price:100,supplier:'Findlay'},{id:3,item:'Fish',category:'Seafood',orderAmount:15,caseSize:3,unit:'Lb',price:40,supplier:'Deodato'},{id:1,item:'Beef',category:undefined,orderAmount:100,caseSize:1,unit:'Lb',price:50,supplier:'Sysco'}]

const result =
  group
    ( products
    , item => item.id
    , (original, duplicate) =>
        ({ ...original, orderAmount: original.orderAmount + duplicate.orderAmount })
    )

console.log(result)
.as-console-wrapper { min-height: 100%; }

[
  {
    "id": 1,
    "item": "Beef",
    "category": "Meat",
    "orderAmount": 105,  // <- grouped
    "caseSize": 1,
    "unit": "Lb",
    "price": 50,
    "supplier": "Sysco"
  },
  {
    "id": 2,
    "item": "Chicken",
    "category": "Meat",
    "orderAmount": 10,
    "caseSize": 2,
    "unit": "Grams",
    "price": 100,
    "supplier": "Findlay"
  },
  {
    "id": 3,
    "item": "Fish",
    "category": "Seafood",
    "orderAmount": 15,
    "caseSize": 3,
    "unit": "Lb",
    "price": 40,
    "supplier": "Deodato"
  }
]

上面我们看到 group 需要一个数组作为输入,returns 一个数组作为输出。如果 group 接受任何 iterable 和 returns 可迭代,我们可以让它更有用。数组是可迭代的,因此它们仍将按预期工作,但其他可迭代对象也可以工作 -

function group(input, grouper, update) {
  const res = new Map
  for (const item of input) {
    const uniq = grouper(item)
    res.set
      ( uniq
      , res.has(uniq)
          ? update(res.get(uniq), item)
          : item
      )
  }
  return res.values()
}

function* products () {
  yield {id:1,item:'Beef',category:'Meat',orderAmount:5,caseSize:1,unit:'Lb',price:50,supplier:'Sysco'}
  yield {id:2,item:'Chicken',category:'Meat',orderAmount:10,caseSize:2,unit:'Grams',price:100,supplier:'Findlay'}
  yield {id:3,item:'Fish',category:'Seafood',orderAmount:15,caseSize:3,unit:'Lb',price:40,supplier:'Deodato'}
  yield {id:1,item:'Beef',category:undefined,orderAmount:100,caseSize:1,unit:'Lb',price:50,supplier:'Sysco'}
}

const grouping =
  group
    ( products()
    , item => item.id
    , (original, duplicate) =>
        ({ ...original, orderAmount: original.orderAmount + duplicate.orderAmount })
    )

for (const item of grouping)
  console.log(item)

注意输出是如何而不是包裹在数组中的 -

{
  "id": 1,
  "item": "Beef",
  "category": "Meat",
  "orderAmount": 105,  // <- grouped
  "caseSize": 1,
  "unit": "Lb",
  "price": 50,
  "supplier": "Sysco"
}
{
  "id": 2,
  "item": "Chicken",
  "category": "Meat",
  "orderAmount": 10,
  "caseSize": 2,
  "unit": "Grams",
  "price": 100,
  "supplier": "Findlay"
}
{
  "id": 3,
  "item": "Fish",
  "category": "Seafood",
  "orderAmount": 15,
  "caseSize": 3,
  "unit": "Lb",
  "price": 40,
  "supplier": "Deodato"
}

要从任何可迭代对象中获取数组,您可以使用 Array.from 函数 -

Array.from(grouping) // => [ ... ]

有更快的方法可以做到这一点,但一个好的起点是循环遍历您的匹配结果并附加该项目(如果它不在列表中)。请务必复制该对象,否则您将更改原始值。

uniqueProducts  = products.reduce((result,item)=> {

   var hit = 0;
   for(var idx=0; idx<result.length; idx++) {
      if (result[idx].id == item.id) {
          hit += 1;
          result[idx].orderAmount += item.orderAmount;
          idx = result.length;
      }
   }

   if (hit === 0) {
       result.push(Object.assign({},item));
   }
   return result;

},[]);

您可以将元素减少到 Map 并累加 orderAmount:

const products = [ { id: 1, item: 'Beef', category: 'Meat', orderAmount: 5, caseSize: 1, unit: 'Lb', price: 50, supplier: 'Sysco' }, { id: 2, item: 'Chicken', category: 'Meat', orderAmount: 10, caseSize: 2, unit: 'Grams', price: 100, supplier: 'Findlay' }, { id: 3, item: 'Fish', category: 'Seafood', orderAmount: 15, caseSize: 3, unit: 'Lb', price: 40, supplier: 'Deodato' }, { id: 1, item: 'Beef', category: undefined, orderAmount: 100, caseSize: 1, unit: 'Lb', price: 50, supplier: 'Sysco' } ]

const reducedProductsMap = products.reduce((map, p) => {
    const previousOrderAmount = map.get(p.id) !== undefined ? map.get(p.id).orderAmount : 0
    let newP = {...p}
    newP.orderAmount += previousOrderAmount
    map.set(newP.id, newP)

    return map
}, new Map());

console.log(Array.from(reducedProductsMap.values()));