Как это сделать быстро и эффективно? Какой способ лучше?

Введение

В JavaScript мы можем легко подсчитать все элементы массива со свойством length, но как мы можем подсчитать элементы, соответствующие определенным условиям? Мы можем добиться этого несколькими способами. Давайте исследовать это!

Тестовые данные

Для нашей цели давайте определим тестовые данные как массив объектов:

const data = [ { a: 1, count: true  }, 
               { b: 2, count: true  }, 
               { c: 4, count: false },
               { d: 8, count: true  } ];

Наш массив имеет общую длину 4, но мы хотим подсчитывать только элементы, помеченные как count: true — в этом случае ожидаемое количество равно 3.

Для итерации цикла

Самый простой способ — перебрать все элементы и подсчитать те, которые соответствуют нашим критериям:

let result = 0;
for (let i = 0; i < data.length; i++) {
    result += Number(data[i].count);
}

// result: 3

Здесь мы инициализируем result как 0 — эта переменная будет содержать наш общий счет. Затем мы перебираем все элементы и добавляем к result элементам count: true. Мы приводим значение boolean к Number, чтобы мы могли легко преобразовать false в 0 и true в 1 соответственно.

Уменьшать

Другой способ посчитать элементы массива и получить в конце единственное значение — использовать метод Array.reduce().

Под капотом reduce перебирает все элементы в заданном массиве и выполняет функцию «редуктор».

const result = data.reduce((acc, d) => acc += Number(d.count), 0);

// result: 3

В приведенном выше коде мы передаем методу reduce два параметра: стрелочную функцию (которая имеет нашу логику подсчета) и 0 в качестве начального значения. Затем reduce выполняет итерацию по всем элементам, вызывая нашу функцию редуктора.

Функция Reducer имеет два входных параметра: acc — аккумулятор, который содержит результат предыдущей итерации и в самом начале устанавливается в наше начальное значение, а d используется как имя, представляющее текущее значение (в нашем случае текущий объект в массиве data).

Наш окончательный результат 3, как и ожидалось.

Фильтр

Наконец, мы можем просто использовать метод filter, который в итоге возвращает новый массив со всеми элементами, соответствующими заданным критериям. Далее мы проверяем длину нового массива, которая в основном равна нашему результату.

const result = data.filter(d => d.count === true).length;

// result: 3

Бенчмаркинг

Во всех наших решениях наша временная сложность линейна O(n), так как мы должны пройти через каждый элемент в массиве. Таким образом, если количество элементов в массиве увеличивается, всем нашим алгоритмам требуется больше времени для обработки каждого элемента.

Если вы хотите узнать больше о нотации Big O, посмотрите мой другой учебник:

Объяснение нотации большого O.

Наконец, мы можем использовать такой инструмент, как JS Bench, и тестировать каждое решение. Давайте сделаем это сейчас! Вы можете посмотреть и поэкспериментировать с ним здесь: Count Specific Array Elements Benchmark

С 4 элементами в массиве тестовых данных результаты почти идентичны, и нет явного победителя, так как каждый раз, когда мы запускаем, результат теста будет немного отличаться:

Однако, как только мы расширим наш массив, чтобы он стал больше, результаты кажутся более воспроизводимыми:

Здесь мы использовали 40 элементов в качестве наших тестовых данных:

const data = Array(40).fill().map(() => { return {a: 1, count: true}; });

Хотя между reduce и for loop нет существенной разницы, решение filter выходит вперед!

Сравнение яблок с апельсинами

Интересно, что поскольку предыдущие решения преобразуют логические значения в числа, а решение с filter их не использует (что может повлиять на производительность), мы можем немного изменить наше «самое быстрое» решение и привести логическое значение к числу.

const result = data.filter(d => Number(d.count) === 1).length;

Давайте посмотрим, как это изменение повлияет на нашу производительность:

Опять же, filter — no type conversion является самым быстрым, но мы ясно видим, что другие решения получают штрафные баллы за использование приведения типа Number. Как только аналогичный код был добавлен в наше самое быстрое решение, он сразу же стал нашим самым медленным решением.

Заключение

Подсчет конкретных элементов в массиве с использованием современного JavaScript — довольно тривиальная задача. Тем не менее, окончательные результаты были немного неожиданными, особенно потому, что самым быстрым решением было использование filter()method.

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

Если вам понравился этот урок, хлопайте в ладоши и следуйте за мной. Пожалуйста, ознакомьтесь с другими моими качественными статьями о программировании.

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .