суммировать большой массив (более 200 000) объектов как можно быстрее

#node.js

#node.js

Вопрос:

В восходящем потоке я поддерживаю высокочастотный вызов API. После вызова у меня будет большой массив объектов, которые мне нужно эффективно суммировать (добавить) в итоговые значения для каждого объекта как можно быстрее, используя nodejs.

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

 [{
samplefield1: 123, 
samplefield2: 345, 
samplefield3: 678, 
samplefield4: 910, 
samplefield5: 111
},
{
samplefield1: 123, 
samplefield2: 345, 
samplefield3: 678, 
samplefield4: 910, 
samplefield5: 111
},
{
samplefield1: 123, 
samplefield2: 345, 
samplefield3: 678, 
samplefield4: 910, 
samplefield5: 111
},
{
samplefield1: 123, 
samplefield2: 345, 
samplefield3: 678, 
samplefield4: 910, 
samplefield5: 111
}.... 
]
  

желаемый результат

 {
samplefield1total: 1349596065934, 
samplefield2total: 5856960650505, 
samplefield3total: 4344343434343, 
samplefield4total: 44444434342910, 
samplefield5total: 79797696969696
}
  

В качестве дополнительного бонуса было бы удивительно, если бы я мог также получить количество всех записей из входных данных, добавленных в массив, как показано ниже…

вывод бонусного задания:

 {
samplefield1total: 1349596065934, 
samplefield2total: 5856960650505, 
samplefield3total: 4344343434343, 
samplefield4total: 44444434342910, 
samplefield5total: 79797696969696,
recordcount: 145634
}
  

но если это не очень эффективно сделать в приведенном выше примере, я могу сделать это снаружи с помощью простого оператора array.length

ценю любую помощь, которую вы можете предложить

Комментарии:

1. Любая операция с большими данными заблокирует цикл событий, возможно, попробуйте рабочие потоки 🙂

2. Гарантируется ли, что каждый объект в большом массиве имеет одинаковые ключи?

3. да, все идентификаторы ключевых полей будут одинаковыми в каждом JSON внутри массива

4. @MattNicholls пожалуйста, не говорите мне, что вы на самом деле получаете все это в формате JSON (в виде огромной строки)

5. Эй, ваш сценарий не совсем понятен. Что посылает вам ваш высокочастотный вызов? новые элементы для этого массива? Вы храните этот массив в памяти или используете какую-то базу данных? Я имею в виду, что самое простое, что я могу придумать, это просто сохранить sums объект с ранее рассчитанными суммами и просто добавлять новые значения по мере их получения, но я не уверен, что это реальный сценарий.

Ответ №1:

Лучшее, что вы можете сделать, это не использовать помощники массива (которые должны выделять фрейм вызова для каждого отдельного экземпляра простого добавления). Простой цикл for — это все, что вам нужно, поэтому вам все равно нужно получить длину массива.

 const data = [{samplefield1: 123, samplefield2: 345, samplefield3: 678, samplefield4: 910, samplefield5: 111},{samplefield1: 123, samplefield2: 345, samplefield3: 678, samplefield4: 910, samplefield5: 111},{samplefield1: 123, samplefield2: 345, samplefield3: 678, samplefield4: 910, samplefield5: 111},{samplefield1: 123, samplefield2: 345, samplefield3: 678, samplefield4: 910, samplefield5: 111}];

let totals = data[0];
let recordcount = data.length;
let keys = Object.keys(totals);
let keycount = keys.length;

for(let i = 1; i < recordcount; i  ) {
  for(let j = 0; j < keycount; j  ) {
    totals[keys[j]]  = data[i][keys[j]];
  }
}

totals.recordcount = recordcount;

console.log(totals);  

К сожалению, вам не удастся избежать этой O(N*M) временной сложности, поскольку требуется получить доступ к каждому из M полей для N объектов, чтобы добавить их.

Как отмечено в комментариях @Shubh, это заблокирует цикл событий. Если это высокочастотный вызов API, каждый вызов которого требует такого большого объема обработки, вам следует передать его в рабочий поток. Если возможно, подумайте, откуда вы получаете данные (это база данных?) и посмотрите, можете ли вы заставить ее выполнять вычисления вместо этого.

Обратите внимание, что если ваши числа будут намного больше, чем ваши данные выборки в post, возможно, вам придется использовать вместо этого BigInts для сумм, поскольку JavaScript обеспечивает такую точность только в своем числовом типе.

Комментарии:

1. хороший отзыв о bigint, я не рассматривал это, но это определенно было бы применимо к некоторым полям. кроме того, я рад, что отказался от этого бонусного задания, поскольку я бы пропустил то, что оно уже записано в разработанном вами цикле. Эти данные поступают непосредственно из dynamodb, поэтому я не верю, что есть возможность заставить его обрабатывать вычисления.

2. есть ли возможность сэкономить время, вычисляя итоговые значения для каждого поля параллельно?

3. @MattNicholls Вы не сможете распараллелить синхронные вычисления, если не поместите каждое поле в отдельный рабочий поток, поскольку JavaScript однопоточный. Я думаю, это вариант, если вы готовы использовать его, но он может потребовать больших затрат памяти, поскольку копия данных должна быть отправлена каждому рабочему.