Как суммировать значения эквивалентных ключей в массиве объектов, где может быть несколько вновь сгенерированных ключей?

0

Вопрос

Существует массив объектов, подобных этому, где есть ключ "категория" и некоторые ключи "серии".

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
    },

]

(Обратите внимание, что "категория" может быть практически любым значением, хотя, вероятно, будет несколько похожих значений, а также некоторые уникальные значения, например, существует несколько объектов со значением "категория ""Категория 3", но только 1 со значением "категория ""Категория 2")

В следующих строках кода будут суммированы все серии 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
    }
]

Однако я хочу иметь возможность добавлять не только серии_1, но и все другие элементы.

В этом примере в качестве ключей используются только категории и серии_1 и серии_2. Тем не менее, могут быть:

  1. серии_3
  2. серии_4
  3. серия 5
  4. серия 6
  5. серия 7
  6. и т.д..

Как я могу учесть все потенциальные серии 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
    }
]
arrays javascript json key-value
2021-11-24 02:19:06
6

Лучший ответ

2

Чтобы обработать логику нескольких свойств, можно выполнить цикл по каждому свойству и проверить, соответствует ли оно регулярному выражению series_\d+. Если это так, вы знаете, что это свойство, которое вам нужно увеличить, и обрабатываете его соответствующим образом (также необходима проверка наличия свойства, как указано Jayce444).

В следующем решении используетсяArray.reduce. В функции редуктора она проверяет, содержит ли массив накопителей элемент с тем же 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)

2021-11-24 02:38:52

Этот подход нарушается, когда все объекты не имеют одинаковых последовательных ключей. Например, если вы добавите series_3: 5 только для первого объекта это заканчивается так series_3: NaN в результате.
Jayce444

Не будет ли это пропускать новые ключи серии, добавленные для той же категории в более поздних объектах? Это также O(n^2) (я думаю)
Phil

@Фил, спасибо за уведомление. Я обновил свой ответ.
Spectric
1

Что - то вроде этого может сработать.

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);

2021-11-24 02:27:40

Не уверен насчет редуктора с побочными эффектами (result мутация)
Phil
0

Создайте карту для сопоставления сумм рядов по категориям.

Затем создайте массив из этой карты с ключами в виде 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; }

2021-11-24 02:32:20
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 } 
  ] 

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 }

2021-11-24 02:47:52
0

Вы можете просто выполнить итерацию по массиву объектов, а затем по ключам каждого объекта, хранящимся в буферном объекте. Вам просто нужно либо проверить наличие каждого ключа и добавить его, если он отсутствует, либо вы можете просто объединить ключи falsey в значение по умолчанию, как это сделал я. Я удаляю ключ категории из объекта после того, как получу его значение, чтобы мне не пришлось пытаться пропустить его во время итерации с ключом.

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);

Если вам не нужно иметь это в массиве, последний шаг является необязательным. Он существует только для преобразования объекта в массив.

2021-11-24 02:31:18
0

Вот как бы я это сделал

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;
}, {});
2021-11-24 03:11:13

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................