Методы итерирования массивов похожи на «стартовые наркотики» (это, конечно, не наркотики; и я не говорю, что наркотики — это хорошо; это — просто фигура речи). Из-за них многие «подсаживаются» на функциональное программирование. Всё дело в том, что они невероятно удобны. Кроме того, большинство этих методов очень просто понять. Методы наподобие .map()
и .filter()
принимают всего один аргумент-коллбэк и позволяют решать простые задачи. Но возникает такое ощущение, что метод .reduce()
у многих вызывает определённые затруднения. Понять его немного сложнее.
Я уже писал о том, почему я думаю, что .reduce()
создаёт множество проблем. Отчасти это происходит из-за того, что многие руководства демонстрируют использование .reduce()
только при обработке чисел. Поэтому я и писал о том, как много задач, не подразумевающих выполнение арифметических операций, можно решать с помощью .reduce()
. Но что если вам совершенно необходимо работать именно с числами?
Типичный случай использования .reduce()
выглядит как вычисление среднего арифметического значения элементов массива. На первый взгляд кажется, что ничего особенного в этой задаче нет. Но она не так уж и проста. Дело в том, что прежде чем посчитать среднее, нужно найти следующие показатели:
- Общая сумма значений элементов массива.
- Длина массива.
Выяснить всё это довольно просто. А вычисление средних значений для числовых массивов — тоже операция не из сложных. Вот элементарный пример:
function average(nums) {
return nums.reduce((a, b) => (a + b)) / nums.length;
}
Как видите, особых непонятностей тут не наблюдается. Но задача становится тяжелее в том случае, если работать приходится с более сложными структурами данных. Что если у нас имеется массив объектов? Что если некоторые объекты из этого массива нужно отфильтровать? Как быть, если из объектов нужно извлечь некие числовые значения? При таком раскладе вычисление среднего значения для элементов массива — это уже задача немного более сложная.
Для того чтобы с этим разобраться мы решим учебную задачу (она основана на этом задании с FreeCodeCamp). Решим мы её пятью разными способами. У каждого из них есть собственные преимущества и недостатки. Разбор этих пяти подходов к решению данной задачи покажет то, каким гибким может быть JavaScript. И я надеюсь, что анализ решений даст вам пищу для размышлений о том, как использовать .reduce()
в реальных проектах.
Обзор задачи
Предположим, что у нас есть массив объектов, описывающих сленговые выражения викторианской эпохи. Нужно отфильтровать те выражения, которые не встречаются в Google Books (свойство found
соответствующих объектов равно false
), и найти среднюю оценку популярности выражений. Вот как могут выглядеть подобные данные (они взяты отсюда):
const victorianSlang = [
term: 'doing the bear',
found: true,
popularity: 108,
},
term: 'katterzem',
found: false,
popularity: null,
},
term: 'bone shaker',
found: true,
popularity: 609,
},
term: 'smothering a parrot',
found: false,
popularity: null,
},
term: 'damfino',
found: true,
popularity: 232,
},
term: 'rain napper',
found: false,
popularity: null,
},
term: 'donkey’s breakfast',
found: true,
popularity: 787,
},
term: 'rational costume',
found: true,
popularity: 513,
},
term: 'mind the grease',
found: true,
popularity: 154,
},
];
Рассмотрим 5 способов нахождения среднего значения оценки популярности выражений из этого массива.
1. Решение задачи без использования .reduce() (императивный цикл)
В нашем первом подходе к решению задачи метод .reduce()
использоваться не будет. Если вы раньше не сталкивались с методами для итерирования массивов, тогда, надеюсь, разбор этого примера немного прояснит для вас ситуацию.
let popularitySum = 0;
let itemsFound = 0;
const len = victorianSlang.length;
let item = null;
for (let i = 0; i < len; i++) {
item = victorianSlang[i];
if (item.found) {
popularitySum = item.popularity + popularitySum;
itemsFound = itemsFound + 1;
}
const averagePopularity = popularitySum / itemsFound;
console.log("Average popularity:", averagePopularity);
Если вы знакомы с JavaScript, то вы без особого труда поймёте этот пример. Собственно говоря, здесь происходит следующее:
- Мы инициализируем переменные
popularitySum
иitemsFound
. Первая переменная,popularitySum
, хранит общую оценку популярности выражений. А вторая переменная,itemsFound
, (вот уж неожиданность) хранит количество найденных выражений. - Затем мы инициализируем константу
len
и переменнуюitem
, которые пригодятся нам при обходе массива. - В цикле
for
счётчикi
инкрементируется до тех пор, пока его значение не достигнет значения индекса последнего элемента массива. - Внутри цикла мы берём элемент массива, который хотим исследовать. К элементу обращаемся с помощью конструкции
victorianSlang[i]
. - Затем мы выясняем, встречается ли данное выражение в коллекции книг.
- Если выражение в книгах встречается — мы берём значение его рейтинга популярности и прибавляем к значению переменной
popularitySum
. - При этом мы ещё и увеличиваем счётчик найденных выражений —
itemsFound
. - И, наконец, мы находим среднее значение, деля
popularitySum
наitemsFound
.
Итак, с задачей мы справились. Возможно, решение у нас получилось не особенно красивое, но своё дело оно делает. Использование методов для итерирования массивов позволит сделать его немного чище. Давайте взглянем на то, удастся ли нам, и правда, «почистить» это решение.
2. Простое решение №1: .filter(), .map() и нахождение суммы с помощью .reduce()
Давайте, перед первой попыткой воспользоваться методами массивов для решения задачи, разобьём её на небольшие части. А именно, вот что нам нужно сделать:
- Отобрать объекты, представляющие выражения, которые имеются в коллекции Google Books. Тут можно воспользоваться методом
.filter()
. - Извлечь из объектов оценки популярности выражений. Для решения этой подзадачи подойдёт метод
.map()
. - Вычислить сумму оценок. Здесь мы можем прибегнуть к помощи нашего старого друга
.reduce()
. - И, наконец, найти среднее значение оценок.
Вот как это выглядит в коде:
// Вспомогательные функции
// ----------------------------------------------------------------------------
function isFound(item) {
return item.found;
};
function getPopularity(item) {
return item.popularity;
}
function addScores(runningTotal, popularity) {
return runningTotal + popularity;
}
// Вычисления
// ----------------------------------------------------------------------------
// Отфильтровываем выражения, которые не были найдены в книгах.
const foundSlangTerms = victorianSlang.filter(isFound);
// Извлекаем оценки популярности, получая массив чисел.
const popularityScores = foundSlangTerms.map(getPopularity);
// Находим сумму всех оценок популярности. Обратите внимание на то, что второй параметр
// указывает на то, что reduce нужно использовать начальное значение аккумулятора, равное 0.
const scoresTotal = popularityScores.reduce(addScores, 0);
// Вычисляем и выводим в консоль среднее значение.
const averagePopularity = scoresTotal / popularityScores.length;
console.log("Average popularity:", averagePopularity);
Приглядитесь к функции addScore
, и к той строке, где вызывается .reduce()
. Обратите внимание на то, что addScore
принимает два параметра. Первый, runningTotal
, известен как аккумулятор. Он хранит сумму значений. Его значение изменяется каждый раз, когда мы, перебирая массив, выполняем оператор return
. Второй параметр, popularity
, представляет собой отдельный элемент массива, который мы обрабатываем. В самом начале перебора массива оператор return
в addScore
ещё ни разу не выполнялся. Это значит, что значение runningTotal
ещё не устанавливалось автоматически. Поэтому, вызывая .reduce()
, мы передаём этому методу то значение, которое нужно записать в runningTotal
в самом начале. Это — второй параметр, переданный .reduce()
.
Итак, мы применили для решения задачи методы итерирования массивов. Новая версия решения получилась гораздо чище, чем предыдущая. Другими словами, решение получилось более декларативным. Мы не сообщаем JavaScript о том, как именно нужно выполнить цикл, не следим за индексами элементов массивов. Вместо этого мы объявляем простые вспомогательные функции маленького размера и комбинируем их. Всю тяжёлую работу делают за нас методы массивов .filter()
, .map()
и .reduce()
. Такой подход к решению подобных задач оказывается более выразительным. Эти методы массивов гораздо полнее, чем это может сделать цикл, сообщают нам о намерении, заложенном в код.
3. Простое решение №2: использование нескольких аккумуляторов
В предыдущей версии решения мы создали целую кучу промежуточных переменных. Например — foundSlangTerms
и popularityScores
. В нашем случае такое решение вполне приемлемо. Но что если мы поставим перед собой более сложную цель, касающуюся устройства кода? Хорошо было бы, если мы могли бы использовать в программе шаблон проектирования «текучий интерфейс» (fluent interface). При таком подходе мы смогли бы объединять в цепочку вызовы всех функций и смогли бы обойтись без промежуточных переменных. Однако тут нас поджидает одна проблема. Обратите внимание на то, что нам необходимо получить значение popularityScores.length
. Если мы собираемся объединить всё в цепочку, тогда нужен какой-то другой способ нахождения количества элементов в массиве. Количество элементов в массиве играет роль делителя при вычислении среднего значения. Посмотрим — сможем ли мы так изменить подход к решению задачи, чтобы всё можно было бы сделать путём объединения вызовов методов в цепочку. Мы сделаем это, отслеживая при переборе элементов массива два значения, то есть — используя «двойной аккумулятор».
// Вспомогательные функции
// ---------------------------------------------------------------------------------
function isFound(item) {
return item.found;
};
function getPopularity(item) {
return item.popularity;
}
// Для представления нескольких значений, возвращаемых return, мы используем объект.
function addScores({totalPopularity, itemCount}, popularity) {
return {
totalPopularity: totalPopularity + popularity,
itemCount: itemCount + 1,
};
}
// Вычисления
// ---------------------------------------------------------------------------------
const initialInfo = {totalPopularity: 0, itemCount: 0};
const popularityInfo = victorianSlang.filter(isFound)
.map(getPopularity)
.reduce(addScores, initialInfo);
// Вычисляем и выводим в консоль среднее значение.
const {totalPopularity, itemCount} = popularityInfo;
const averagePopularity = totalPopularity / itemCount;
console.log("Average popularity:", averagePopularity);
Здесь мы, для работы с двумя значениями, воспользовались в функции-редьюсере объектом. При каждом проходе по массиву, выполняемом с помощью addScrores
, мы обновляем общее значение рейтинга популярности и количество элементов. Важно обратите внимание на то, что эти два значения представлены в виде одного объекта. При таком подходе мы можем «обмануть» систему и хранить две сущности внутри одного возвращаемого значения.
Функция addScrores
получилась немного более сложной, чем функция с таким же именем предыдущего примера. Но теперь оказывается так, что мы можем использовать единственную цепочку вызовов методов для выполнения всех операций с массивом. В результате обработки массива получается объект popularityInfo
, который хранит всё, что нужно для нахождения среднего. Это делает цепочку вызовов аккуратной и простой.
Если вы чувствуете в себе желание улучшить этот код, то вы можете с ним поэкспериментировать. Например — можете переделать его так, чтобы избавиться от множества промежуточных переменных. Этот код можно даже попытаться уложить в одну строчку.
4. Композиция функций без использования точечной нотации
Если вы — новичок в функциональном программировании, или если вам кажется, что функциональное программирование — это слишком сложно, вы можете пропустить этот раздел. Его разбор принесёт вам пользу в том случае, если вы уже знакомы с curry()
и compose()
. Если вы хотите углубиться в данную тему — взгляните на этот материал о функциональном программировании на JavaScript, и, в частности, на третью часть серии, в которую он входит.
Мы — программисты, которые придерживаются функционального подхода. Это значит, что мы стремимся к тому, чтобы строить сложные функции из других функций — маленьких и простых. До сих пор мы, в ходе рассмотрения разных вариантов решения задачи, уменьшали количество промежуточных переменных. В результате код решения становился всё проще и проще. Но что если довести эту идею до крайности? Что если попытаться избавиться от всех промежуточных переменных? И даже попробовать уйти от некоторых параметров?
Можно создать функцию для вычисления среднего значения с использованием одной лишь функции compose()
, без использования переменных. Мы называем это «программированием без использования точеной нотации» или «неявным программированием». Для того чтобы писать подобные программы понадобится множество вспомогательных функций.
Иногда такой код шокирует людей. Это происходит из-за того, что подобный подход сильно отличается от общепринятого. Но я выяснил, что написание кода в стиле неявного программирования является одним из самых быстрых способов вникнуть в сущность функционального программирования. Поэтому я могу вам посоветовать попробовать эту методику в каком-нибудь личном проекте. Но хочу сказать, что, возможно, не стоит писать в стиле неявного программирования тот код, который придётся читать другим людям.
Итак, вернёмся к нашей задаче по построению системы вычисления средних значений. Ради экономии места мы перейдём здесь на использование стрелочных функций. Обычно, как правило, лучше использовать именованные функции. Вот хорошая статья на эту тему. Это позволяет получить более качественные результаты трассировки стека в случае возникновения ошибок.
// Вспомогательные функции
// ----------------------------------------------------------------------------
const filter = p => a => a.filter(p);
const map = f => a => a.map(f);
const prop = k => x => x[k];
const reduce = r => i => a => a.reduce(r, i);
const compose = (...fns) => (arg) => fns.reduceRight((arg, fn) => fn(arg), arg);
// Это - так называемый "blackbird combinator".
// Почитать о нём можно здесь: https://jrsinclair.com/articles/2019/compose-js-functions-multiple-parameters/
const B1 = f => g => h => x => f(g(x))(h(x));
// Вычисления
// ----------------------------------------------------------------------------
// Создадим функцию sum, которая складывает элементы массива.
const sum = reduce((a, i) => a + i)(0);
// Функция для получения длины массива.
const length = a => a.length;
// Функция для деления одного числа на другое.
const div = a => b => a / b;
// Мы используем compose() для сборки нашей функции из маленьких вспомогательных функций.
// При работе с compose() код надо читать снизу вверх.
const calcPopularity = compose(
B1(div)(sum)(length),
map(prop('popularity')),
filter(prop('found')),
);
const averagePopularity = calcPopularity(victorianSlang);
console.log("Average popularity:", averagePopularity);
Если весь этот код кажется вам полной бессмыслицей — не беспокойтесь об этом. Я включил его сюда в виде интеллектуального упражнения, а не для того, чтобы вас расстраивать.
В данном случае основная работа идёт в функции compose()
. Если прочесть её содержимое снизу вверх, то окажется, что вычисления начинаются с фильтрации массива по свойству его элементов found
. Затем мы извлекаем свойство элементов popularity
с помощью map()
. После этого мы используем так называемый «blackbird combinator». Эта сущность представлена в виде функции B1
, которая используется для выполнения двух проходов вычислений над одним набором входных данных. Для того чтобы лучше в этом разобраться, взгляните на эти примеры:
// Все строки кода, представленные ниже, эквивалентны:
const avg1 = B1(div)(sum)(length);
const avg2 = arr => div(sum(arr))(length(arr));
const avg3 = arr => ( sum(arr) / length(arr) );
const avg4 = arr => arr.reduce((a, x) => a + x, 0) / arr.length;
Опять же, если вы снова ничего не поняли — не беспокойтесь. Это — просто демонстрация того, что на JavaScript можно писать очень разными способами. Из таких вот особенностей и складывается красота этого языка.
5. Решение задачи за один проход с вычислением кумулятивного среднего значения
Все вышеприведённые программные конструкции хорошо справляются с решением нашей задачи (включая императивный цикл). Те из них, в которых используется метод .reduce()
, имеют кое-что общее. Они основаны на разбиении проблемы на небольшие фрагменты. Эти фрагменты потом различными способами компонуются. Анализируя эти решения, вы могли заметить, что в них мы обходим массив три раза. Возникает такое чувство, что это неэффективно. Хорошо было бы, если бы существовал способ обработки массива и выдачи результата за один проход. Такой способ существует, но его применение потребует прибегнуть к математике.
Для того чтобы вычислить среднее значение для элементов массива за один проход, нам понадобится новый метод. Нужно найти способ вычисления среднего с использованием ранее вычисленного среднего и нового значения. Поищем этот способ с помощью алгебры.
Среднее значение n
чисел можно найти, воспользовавшись такой формулой:
Для того чтобы узнать среднее n + 1
чисел подойдёт та же формула, но в другой записи:
Эта формула представляет собой то же самое, что вот это:
И то же самое, что это:
Если немного это преобразовать, то получится следующее:
Если вы не видите во всём этом смысла — ничего страшного. Итог всех этих преобразований сводится к тому, что с помощью последней формулы мы можем рассчитывать среднее значение в процессе однократного обхода массива. Для этого нужно знать значение текущего элемента, среднее значение, вычисленное на предыдущем шаге, и число элементов. Кроме того, большинство вычислений можно вынести в функцию-редьюсер:
// Функция для вычисления среднего значения
// ----------------------------------------------------------------------------
function averageScores({avg, n}, slangTermInfo) {
if (!slangTermInfo.found) {
return {avg, n};
return {
avg: (slangTermInfo.popularity + n * avg) / (n + 1),
n: n + 1,
};
}
// Вычисления
// ----------------------------------------------------------------------------
// Вычисляем и выводим в консоль среднее значение.
const initialVals = {avg: 0, n: 0};
const averagePopularity = victorianSlang.reduce(averageScores, initialVals).avg;
console.log("Average popularity:", averagePopularity);
Благодаря использованию этого подхода необходимое значение можно найти, обойдя массив всего один раз. Другие подходы используют один проход для фильтрации массива, ещё один — для извлечения из него нужных данных, и ещё один — для нахождения суммы значений элементов. Здесь же всё укладывается в один проход по массиву.
Обратите внимание на то, что это необязательно делает вычисления более эффективными. При таком подходе приходится выполнять больше вычислений. Мы, при поступлении каждого нового значения, выполняем операции умножения и деления, делая это для поддержания текущего значения среднего в актуальном состоянии. В других вариантах решения этой задачи мы делим одно число на другое лишь один раз — в конце программы. Но такой подход гораздо эффективнее в плане использования памяти. Промежуточные массивы здесь не используются, в результате нам приходится хранить в памяти лишь объект с двумя значениями.
Однако такая вот эффективность использования памяти имеет определённую цену. Теперь в одной функции мы выполняем три действия. Мы в ней фильтруем массив, извлекаем число и пересчитываем результат. Это усложняет функцию. В результате, взглянув на код, уже не так просто его понять.
Что выбрать?
Какой же из рассмотренных выше пяти подходов к решению задачи можно назвать самым лучшим? На самом деле, это зависит от многих факторов. Возможно, вам нужно обработать по-настоящему длинный массив. Или, возможно, вашему коду нужно выполняться на платформе, на которой доступно не особенно много памяти. В подобных случаях имеет смысл воспользоваться тем решением задачи, где обработка массива выполняется за один проход. Но если системные ограничения роли не играют, тогда можно с успехом пользоваться более выразительными подходами к решению задачи. Программисту нужно проанализировать собственную ситуацию и принять решение о том, что лучше всего подходит его приложению, что наиболее целесообразно использовать в его обстоятельствах.
Возможно, у кого-то сейчас возникнет вопрос о том, есть ли способ объединения преимуществ разных подходов к решению подобной задачи. Можно ли разбить задачу на мелкие части, но выполнять все вычисления за один проход по массиву? Сделать это можно. Для этого понадобится применить концепцию трансдьюсеров. Это — отдельная большая тема.
Итоги
Мы рассмотрели пять способов вычисления среднего для элементов массива:
- Без использования
.reduce()
. - С использованием методов
.filter()
и.map()
, а также — метода.reduce()
в роли механизма для нахождения суммы чисел. - С использованием аккумулятора, являющегося объектом и хранящего несколько значений.
- С применением методики неявного программирования.
- С вычислением кумулятивного среднего при однократном проходе по массиву.
Что же, всё-таки, стоит выбрать для практического применения? На самом деле — вам решать. Но если вы хотите найти какую-то подсказку — поделюсь своим мнением о том, как можно принять решение о наиболее подходящем способе решения задачи:
- Начните с использования подхода, который вы понимаете лучше всего. Если он позволяет достичь цели — остановитесь на нём.
- Если существует некий подход, который вы не понимаете, но хотите изучить — решите задачу с его помощью.
- И, наконец, если вы столкнулись с проблемой нехватки памяти — попробуйте тот вариант, где массив обходится один раз.
Уважаемые читатели! Как вы чаще всего обрабатываете массивы в JavaScript-проектах?
Let’s imagine we have an array of integers like this:
var values = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
The average is obtained with the following formula
A= (1/n)Σxi ( with i = 1 to n ) … So: x1/n + x2/n + … + xn/n
We divide the current value by the number of values and add the previous result to the returned value.
The reduce method signature is
reduce(callback[,default_previous_value])
The reduce callback function takes the following parameters:
- p : Result
of the previous calculation - c : Current value (from the current index)
- i : Current array element’s index value
- a : The current reduced Array
The second reduce’s parameter is the default value … (Used in case the array is empty ).
So the average reduce method will be:
var avg = values.reduce(function(p,c,i,a){return p + (c/a.length)},0);
If you prefer you can create a separate function
function average(p,c,i,a){return p + (c/a.length)};
function sum(p,c){return p + c)};
And then simply refer to the callback method signature
var avg = values.reduce(average,0);
var sum= values.reduce(sum,0);
Or Augment the Array prototype directly..
Array.prototype.sum = Array.prototype.sum || function (){
return this.reduce(function(p,c){return p+c},0);
};
It’s possible to divide the value each time the reduce method is called..
Array.prototype.avg = Array.prototype.avg || function () {
return this.reduce(function(p,c,i,a){return p+(c/a.length)},0);
};
Or even better , using the previously defined Array.protoype.sum()
method, optimize the process my calling the division only once 🙂
Array.prototype.avg = Array.prototype.avg || function () {
return this.sum()/this.length;
};
Then on any Array object of the scope:
[2, 6].avg();// -> 4
[2, 6].sum();// -> 8
NB: an empty array with return a NaN wish is more correct than 0 in my point of view and can be useful in specific use cases.
- js массивы
Ответы
Чтобы найти среднее арифметическое элементов массива, нужно сумму элементов массива разделить на длину массива. Для нахождения суммы элементов массива можно использовать функцию высшего порядка или цикл.
const getAverage = (numbers) => {
const sum = numbers.reduce((acc, number) => acc + number, 0);
const length = numbers.length;
return sum / length;
};
const numbers = [1, 2, 3, 4];
console.log(getAverage(numbers)); // => 2.5
0
0
Хотел бы предложить вариант нахождения среднего арифметического массива с использованием цикла for
:
const arr = [1, 3, 5, 7, 9, 11];
const getAverage = (numbers) => {
let sum = 0; // объявляем переменную, в которой будет храниться сумма всех чисел массива
for (let i = 0; i < numbers.length; i += 1) { // инициализируем цикл
sum += numbers[i]; // на каждой итерации прибавляем к сумме значение текущего элемента массива
}
return sum / numbers.length; // возвращаем среднее арифметическое
};
console.log(getAverage(arr)); // => 6
3
0
Inline версия первого варианта
const getAverage = (numbers) => numbers.reduce((acc, number) => acc + number, 0) / numbers.length
console.log(getAverage([1, 2, 3, 4])) // => 2.5
0
0
Добавьте ваш ответ
Рекомендуемые курсы
26 часов
Старт в любое время
11 часов
Старт в любое время
18 часов
Старт в любое время
Похожие вопросы
I’ve been looking and haven’t found a simple question and answer on stack overflow looking into finding the average of an array.
This is the array that I have
const grades = [80, 77, 88, 95, 68];
I first thought that the answer to this problem would be something like this:
let avg = (grades / grades.length) * grades.length
console.log(avg)
However, this gave me an output of NaN.
So then I tried this:
for (let grade of grades)
avg = (grade / grades.length) * grades.length
console.log(avg)
This gave me an output of 68. (I’m not sure why).
So with this I have two questions. 1. Why was my output 68? and 2. Could somebody help me out with actually finding the average of an array?
undefined
1,01912 silver badges24 bronze badges
asked Apr 9, 2015 at 16:47
0
With ES6 you can turn Andy’s solution into as a one-liner:
const average = array => array.reduce((a, b) => a + b) / array.length;
console.log(average([1,2,3,4,5]));
KetZoomer
2,6293 gold badges14 silver badges43 bronze badges
answered Jan 3, 2017 at 20:59
AustinAustin
6,0515 gold badges16 silver badges12 bronze badges
6
You calculate an average by adding all the elements and then dividing by the number of elements.
var total = 0;
for(var i = 0; i < grades.length; i++) {
total += grades[i];
}
var avg = total / grades.length;
The reason you got 68 as your result is because in your loop, you keep overwriting your average, so the final value will be the result of your last calculation. And your division and multiplication by grades.length cancel each other out.
answered Apr 9, 2015 at 16:50
bwrogabwroga
5,3592 gold badges23 silver badges25 bronze badges
3
For the second part of your question you can use reduce
to good effect here:
const grades = [80, 77, 88, 95, 68];
function getAvg(grades) {
const total = grades.reduce((acc, c) => acc + c, 0);
return total / grades.length;
}
const average = getAvg(grades);
console.log(average);
The other answers have given good insight into why you got 68, so I won’t repeat it here.
answered Apr 9, 2015 at 16:51
AndyAndy
61.1k13 gold badges67 silver badges95 bronze badges
2
The MacGyver way, just for lulz
var a = [80, 77, 88, 95, 68];
console.log(eval(a.join('+'))/a.length)
Pikamander2
7,1223 gold badges48 silver badges68 bronze badges
answered Apr 6, 2017 at 16:43
MihaiMihai
26.1k7 gold badges66 silver badges81 bronze badges
4
There’s no built in function, but you can use this to get the sum,
Array.prototype.sum = function() {
return this.reduce(function(a, b) {return a+b});
};
then divide by the array’s length, e.g.:
var arr = [1, 2, 3, 4, 5];
console.log(arr.sum() / arr.length)
answered Nov 2, 2015 at 3:07
baaobaao
71k17 gold badges141 silver badges202 bronze badges
It can simply be done with a single reduce operation as follows;
var avg = [1,2,3,4].reduce((p,c,_,a) => p + c/a.length,0);
console.log(avg)
answered May 29, 2016 at 6:17
ReduRedu
24.8k6 gold badges55 silver badges74 bronze badges
2
var total = 0
grades.forEach(function (grade) {
total += grade
});
console.log(total / grades.length)
answered Apr 9, 2015 at 16:54
0
The average function you can do is:
const getAverage = (arr) => arr.reduce((p, c) => p + c, 0) / arr.length
Also, I suggest that use the popoular open source tool, eg. Lodash
:
const _ = require('lodash')
const getAverage = (arr) => _.chain(arr)
.sum()
.divide(arr.length)
.round(1)
.value()
answered May 15, 2017 at 8:40
1
You can use map/reduce functions of javascript to find average. Reduce will sum them up, and map will find average.
var avg = grades.map((c, i, arr) => c / arr.length).reduce((p, c) => c + p);
answered Feb 11, 2016 at 21:22
Vlad BezdenVlad Bezden
82k24 gold badges246 silver badges179 bronze badges
1
To calculate the average of an array in JavaScript:
- Sum all the values of the array.
- Divide the sum by the length of the array.
For example:
const arr = [1, 2, 3, 4, 5]; const average = arr.reduce((a, b) => a + b, 0) / arr.length; console.log(average);
Output:
3
This is the quick answer.
However, if this does not ring any bells, I recommend reading further.
In this guide, you learn how to calculate the average of an array of numbers in three different ways:
- The for loop approach
- The forEach() function
- The reduce() function
Before jumping into these, let’s make sure you understand what it means to calculate the average in the first place.
Average in Mathematics
In mathematics, the average is the middle value of a group of numbers. The average is also commonly called the mean or the arithmetic mean.
The average is calculated by dividing the sum of all the values by the number of values.
Example. Find the average age of a group of students in this table:
Name | Age |
---|---|
Alice | 32 |
Bob | 25 |
Charlie | 23 |
David | 24 |
Eric | 26 |
Solution. Let’s sum up the ages and divide the result by the number of students:
s = 32 + 25 + 23 + 24 + 26 = 130 n = 5 avg = s / n = 130 / 5 = 26
Calculating the average is a useful way to describe data in statistics.
For instance, you can calculate the average of your grades to get an idea of how well your studies are going.
Anyway, let’s jump into the coding part of this tutorial.
Let’s implement a basic JavaScript for loop to find the average first.
Find the Average with For Loop in JavaScript
Perhaps the most beginner-friendly way to find the average of a JavaScript array is by using a for loop for summing up the numbers.
Here is how to use a for loop to find the average of an array of numbers:
- Create an array of numbers.
- Initialize the sum variable to accumulate the numbers.
- Loop through the numbers and add each number to the sum.
- Divide the sum of numbers by the number of numbers in the array.
- Show the result.
For example:
const arr = [1, 2, 3, 4, 5]; var sum = 0; for (var number of arr) { sum += number; } average = sum / arr.length; console.log(average);
Output:
3
Although there is nothing wrong with this approach, we can make it way shorter and more concise.
Next up, you are going to learn how to use the forEach() function for calculating the average.
The following examples are more intermediate. Do not worry if you find them a bit tricky as a beginner!
Find the Average Using forEach()
In JavaScript, there is a built-in function called forEach().
This function is used to run a give function for each element in the array.
In other words, the forEach() function behaves a lot like a for loop. It iterates through the array and calls the given function for each element.
To find the average using forEach() function:
- Create an array of numbers.
- Initialize the sum to 0.
- Use forEach() function to loop through the numbers and add each number to the sum.
- Divide the sum of numbers by the number of numbers in the array.
- Show the result.
To make the forEach() add each number to the sum, it needs to take a function that accepts a number argument and adds it to the sum.
For example:
const arr = [1, 2, 3, 4, 5]; var sum = 0; arr.forEach(function(num) { sum += num }); average = sum / arr.length; console.log(average);
Output:
3
To make it a bit more concise, you can also use the arrow function notation in the forEach() function call:
const arr = [1, 2, 3, 4, 5]; var sum = 0; arr.forEach((num) => { sum += num }); average = sum / arr.length; console.log(average);
Although this approach is a bit shorter than the for loop approach, it is still not the most elegant one.
Next, let’s use the reduce() function to find the average of an array of numbers.
This will make the process significantly more straightforward.
Find the Average Using reduce()
In JavaScript, there is a built-in function called reduce().
This function works the same way as folding in mathematics. Reducing means you fold a group of numbers into a single value.
For example, you can reduce or fold an array of numbers to get the sum of it.
The reduce() function takes a function as its argument. This is called a reducer.
It also takes an initial value for the accumulated result as its second argument.
Here is how the reduce() function and the reducer function operate on an array:
- Call a reducer on the first two elements of the array to get a partial result.
- Call the same function on the partial result and the third element of the array to create a new result.
- Repeat until there are no more values left.
- Return the result.
Now, let’s use the reduce() to find the average of a function.
For example:
const arr = [1, 2, 3, 4, 5]; const average = arr.reduce((a, b) => a + b, 0) / arr.length; console.log(average);
Output:
3
Let’s take a moment to understand how this piece of code works:
- The reducer (a, b) => a + b is called for each number in the array, where
- a is the “result so far”.
- b is the next number.
- The second argument of the reduce() function call is 0. This is the initial value for the sum of the array.
- The reduce() function is used to find the sum of the numbers in the array.
- The sum is then divided by the length of the array to get the average.
As you can see, this piece of code is way shorter than the for-loop approach. Once you learn how to read the reduce() function a bit better, you notice how much simpler this piece of code is than the lengthy for loop.
To find a complete guide on how to use the reduce() function in JavaScript, feel free to read this article.
This completes the example.
Now you should have a great understanding of how to calculate the average of an array in JavaScript in a bunch of different-looking ways.
Before you go, let’s do a quick recap.
Conclusion
Today you learned how to calculate the average of an array in JavaScript.
To recap, the average is used in statistics to describe the centrality of a dataset.
To find the average (the mean), sum up the values and divide the sum by the number of values.
In JavaScript, there is a bunch of ways in which you can find the average. In this guide, you saw three common examples:
- For loop
- The forEach() function
- The reduce() function.
The latter approach is a bit tricky to wrap your head around as a beginner. However, it is a really short and concise way to get the job done.
Thanks for reading.
Happy coding!
Further Reading
JavaScript Interview Questions
About the Author
-
I’m an entrepreneur and a blogger from Finland. My goal is to make coding and tech easier for you with comprehensive guides and reviews.
Recent Posts