如何在可以有多个新生成的键的对象数组中对等效键的值求和?

How to sum values of equivalent keys in an array of objects where there can be multiple, newly generated keys?

存在一个像这样的对象数组,其中有一个 'category' 键和一些 'series' 键。

arrOne = [
    {
        "series_1": 25,
        "category": "Category 1",
        "series_2": 50
    },
    {
        "series_1": 11,
        "category": "Category 2",
        "series_2": 22
    },
    {
        "series_1": 32,
        "category": "Category 1",
        "series_2": 74
    },
    {
        "series_1": 74,
        "category": "Category 3",
        "series_2": 98
    },
    {
        "series_1": 46,
        "category": "Category 3",
        "series_2": 29
    },

]

(请注意,'category' 几乎可以是任何值,尽管可能会有多个相似的值以及一些独特的值,例如,有多个具有 'category' 值的对象 'Category 3' 但只有 1 个具有 'category' 值 'Category 2')

下面的代码行将把具有相同类别

的对象的所有series_1相加
        var objForAllCategories = {};
        this.arrOne.forEach(item => {
            if (objForAllCategories.hasOwnProperty(item.category))
                objForAllCategories[item.category] = objForAllCategories[item.category] + item.series_1;
            else
                objForAllCategories[item.category] = item.series_1;
        });
        for (var prop in objForAllCategories) {
            this.allCategoriesAndValues.push({ 
                category: prop, 
                series_1: objForAllCategories[prop] 
            });
        }

所以它会导致:

allCategoriesAndValues = [
    {
        "category": "Category 1",
        "series_1": 57       // 25 + 32 adding up series_1 from all 'Category 1' items in arrOne
    },
    {
        "category": "Category 2",
        "series_1": 11      // only 1 'Category 2' from arrOne
    },
    {
        "category": "Category 3",
        "series_1": 120     // 74 + 46 adding up series_1 from all 'Category 3' items in arrOne
    }
]

但是,我不仅希望能够添加 series_1,还希望能够添加所有其他项目。

这个例子只有类别和 series_1 和 series_2 作为键。但是,可能有:

  1. series_3
  2. series_4
  3. series_5
  4. series_6
  5. series_7
  6. 等..

我如何解释所有潜在的 series_x?

预期结果:

allCategoriesAndValues = [
    {
        "category": "Category 1",
        "series_1": 57,
        "series_2": 124,
        ..... if 'series_3', 'series_4' etc. existed, it would be included in this as above
    },
    {
        "category": "Category 2",
        "series_1": 11,
        "series_2": 22,
        ..... if 'series_3', 'series_4' etc. existed, it would be included in this as above
    },
    {
        "category": "Category 3",
        "series_1": 120,
        "series_2": 127,
        ..... if 'series_3', 'series_4' etc. existed, it would be included in this as above
    }
]

要处理多个属性逻辑,您可以遍历每个 属性 并检查它是否与正则表达式 series_\d+ 匹配。如果是,您知道它是一个需要递增的 属性,并相应地处理它(属性 存在性检查也是必要的,正如 Jayce444 所指出的那样)。

以下解决方案使用Array.reduce。在 reducer 函数中,它检查累加器数组是否包含与当前正在循环的项具有相同 category 属性 的项。如果是这样,它将增加适当的属性。否则,它会将当前项推送到累加器数组。

arrOne=[{series_1:25,category:"Category 1",series_2:50},{series_1:11,category:"Category 2",series_2:22},{series_1:32,category:"Category 1",series_2:74},{series_1:74,category:"Category 3",series_2:98},{series_1:46,category:"Category 3",series_2:29,series_3:50}];

const res = arrOne.reduce((a, b) => {
  let found = a.find(e => e.category == b.category)
  if (found) {
    Object.keys(b).forEach(e => {
      if (/series_\d+/g.test(e)) found[e] = found[e] ? found[e] + b[e] : b[e];
    })
  } else {
    a.push(b)
  }
  return a;
}, [])

console.log(res)

创建地图以按类别整理系列总和。

然后从该映射创建一个数组,键为 category

const arr1 = [{"series_1":25,"category":"Category 1","series_2":50},{"series_1":11,"category":"Category 2","series_2":22},{"series_1":32,"category":"Category 1","series_2":74},{"series_1":74,"category":"Category 3","series_2":98},{"series_1":46,"category":"Category 3","series_2":29}]

const t1 = performance.now()

const cats = arr1.reduce((map, { category, ...series }) =>
  map.set(category, Object.entries(series)
    .reduce((s, [ key, count ]) => ({
      ...s,
      [ key ]: (s[key] ?? 0) + count
    }), map.get(category) ?? {})
  ), new Map())

const allCategoriesAndValues = Array.from(cats, ([ category, series ]) => ({
  category,
  ...series
}))

const t2 = performance.now()

console.info(allCategoriesAndValues)
console.log(`Took ${t2 - t1}ms`)
.as-console-wrapper { max-height: 100% !important; }

类似这样的方法可行。

arrOne = [ { "series_1": 25, "category": "Category 1", "series_2": 50 }, { "series_1": 11, "category": "Category 2", "series_2": 22 }, { "series_1": 32, "category": "Category 1", "series_2": 74 }, { "series_1": 74, "category": "Category 3", "series_2": 98 }, { "series_1": 46, "category": "Category 3", "series_2": 29 },];

const result = [];
arrOne.reduce((acc, {category, ...series}) => {
  if (acc.has(category)) {
    Object.entries(series).forEach(([key, value]) => {
      if (key.startsWith('series_')) {
        acc.get(category)[key] = (acc.get(category)[key] || 0) + value;
      }
    });
  } else {
    const item = {category, ...series};
    result.push(item);
    acc.set(category, item);
  }
  return acc;
}, new Map());

console.log(result);

我会这样做...

const arrOne = 
  [ { series_1: 25, category: 'Category 1', series_2: 50 } 
  , { series_1: 11, category: 'Category 2', series_2: 22 } 
  , { series_1: 32, category: 'Category 1', series_2: 74 } 
  , { series_1: 74, category: 'Category 3', series_2: 98 } 
  , { series_1: 46, category: 'Category 3', series_2: 29 } 
  ] 

console.time('chrono')

const allCategoriesAndValues =
  Object.entries(
  arrOne.reduce((r,{ category, ...series })=>
    {
    let cat = r[category] = r[category] ?? {} 
    Object.entries(series).forEach(([sName,val]) => cat[sName] = (cat[sName] ?? 0) + val);
    return r
    },{})
  ).map(([category,series])=>({category,...series}))

console.timeEnd('chrono')

console.log( allCategoriesAndValues )
.as-console-wrapper {max-height: 100%!important;top:0 }

您可以只遍历对象数组,然后遍历每个对象的键,存储到缓冲区对象中。您只需要检查每个键是否存在并在缺少时添加它,或者您可以像我一样将错误的键合并为默认值。我在获取对象的值后从对象中删除类别键,这样我就不必在键迭代期间尝试跳过它。

const arrOne = [
  {"series_1": 25, "category": "Category 1", "series_2": 50},
  {"series_1": 11, "category": "Category 2", "series_2": 22},
  {"series_1": 32, "category": "Category 1", "series_2": 74},
  {"series_1": 74, "category": "Category 3", "series_2": 98},
  {"series_1": 46, "category": "Category 3", "series_2": 29},
];

let buffer = {};
arrOne.forEach(i=>{
  let c = i.category;
  buffer[c] = buffer[c] || {};
  delete i.category;
  Object.keys(i).forEach(k=>{
    buffer[c][k] = buffer[c][k] || 0;
    buffer[c][k] += i[k];
  });
});

console.log(buffer);

let final = Object.keys(buffer).map(k=>{return {[k]: buffer[k]}});
console.log(final);

如果您不需要在数组中包含它,则最后一步是可选的。它的存在只是为了将对象转化为数组。

这是我的做法

const res = arrOne.reduce((acc, { category, ...vals }) => {
    if (acc[category]) {
        Object.entries(vals).forEach(([ key, val ]) => acc[category][key] = acc[category][key] ? acc[category][key] + val : val);

    } else {
        acc[category] = vals;

    }

    return acc;
}, {});