CODEGURU

ES-6 вспомогательные функции для работы с массивами

Итак, ES-6 подарил нам целых 7 новых функций для работы с массивами:

  1. Функция forEach
  2. Функция map
  3. Функция filter
  4. Функция reduce
  5. Функция find
  6. Функция every
  7. Функция some

Пойдём по порядку.

Функция forEach

Функцию forEach можно рассматривать как альтернативу привычному циклу for. Всё, что она делает — это перебирает каждый элемент массива и вызывает для него функцию-итератор.

Синтаксис

array.forEach(function(currentValue, index, arr), thisValue)

Принимаемые аргументы

  • currentValue – Обязательный аргумент. Текущий элемент массива;
  • index – Опциональный аргумент. Индекс текущего элемента массива;
  • arr – Опциональный аргумент. Оригинал массива;
  • thisValue – Опциональный аргумент. Объект, который нужно использовать как this

Посмотрим на примере, как это работает. Допустим, у нас есть массив с рецептами, в котором у каждого рецепта есть название и список ингредиентов.

const recipes = [
  {
    title: "Омлет",
    ingredients: [
      "яйца", "сливки", "сливочное масло", "соль"
    ]
  },
  {
    title: "Картофельное пюре",
    ingredients: [
      "картофель", "сливочное масло", "молоко", "соль"
    ]
  }
]

// ИСПОЛЬЗУЕМ ТРАДИЦИОННЫЙ FOR ЦИКЛ
for (let i = 0; i < recipes.length; i++) {
  console.log(recipes[i].title); // Омлет, Картофельное пюре
}

// ТО ЖЕ САМОЕ С forEach
recipes.forEach((item) => {
  console.log(item.title); // Омлет, Картофельное пюре
})

Очевидно, что запись forEach короче и легче для чтения. Это, возможно, не так очевидно на примере выше. Но давайте возьмём более приближенный к реальности случай и выведем список ингредиентов для каждого рецепта. С for циклом это будет выглядеть так:

for (let i = 0; i < recipes.length; i++) {
  let recipe = recipes[i];
  for (let j = 0; j < recipe.ingredients.length; j++) {
    let ingredient = recipe.ingredients[j];
    console.log(ingredient)
  }
}

Довольно громоздкий код, две временные переменные i и j. Легко запутаться и допустить ошибку. А вот как тот же результат получить с помощью forEach:

recipes.forEach((recipe) => {
  recipe.ingredients.forEach((ingredient) => {
    console.log(ingredient);
  })
})

Согласитесь, здесь преимущество forEach уже более очевидно. В общем смело можете вместо for циклов использовать forEach. Единственный случай, когда for цикл не удастся заменить — это если вам нужно при достижении какого-то условия прекратить выполнение цикла и выйти из него. В случае с for циклом можно использовать break. При использовании forEach такой возможности нет.

Функция map

Функция map создаёт новый массив из элементов исходного массива, принимая в качестве аргумента функцию и выполняя её для каждого элемента. И всё это без влияния на первоначальный массив.

Синтаксис

array.map(function(currentValue, index, arr), thisValue)

Принимаемые аргументы

  • currentValue – Обязательный аргумент. Текущий элемент массива;
  • index – Опциональный аргумент. Индекс текущего элемента массива;
  • arr – Опциональный аргумент. Оригинал массива;
  • thisValue – Опциональный аргумент. Объект, который нужно использовать как this

Ну и естественно для большей наглядности рассмотрим пару примеров. Пусть у нас есть массив номеров и нам нужно получить новый массив с удвоенными значениями номеров.

const numbers = [1, 2, 3, 4, 5, 6];

//БЕЗ MAP
const doubledNumbers = [];

for (let i = 0; i < numbers.length; i++) {
  doubeldBumbers.push(numbers[i] * 2);
}

// С MAP
const mapDoubledNumber = numbers.map((number) => number * 2);

Как видите, функция map также отличается лаконичностью. Но главное, она всегда возвращает новый массив такого же размера, как исходный.

Функция filter

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

Синтаксис

array.filter(function(currentValue, index, arr), thisValue)

Принимаемые аргументы

  • currentValue – Обязательный аргумент. Текущий элемент массива;
  • index – Опциональный аргумент. Индекс текущего элемента массива;
  • arr – Опциональный аргумент. Оригинал массива;
  • thisValue – Опциональный аргумент. Объект, который нужно использовать как this

Ну и как обычно, пример. Допустим нам нужно из массива c рецептами получить только вегетарианские рецепты. Вы уже догадались, что сейчас я работаю над приложением с рецептами? Да, так что примеры из реальной жизни. )

const recipes = [
  {
    title: "Омлет",
    ingredients: [
      "яйца", "сливки", "сливочное масло", "соль"
    ],
    isVegeterian: true
  },
  {
    title: "Картофельное пюре",
    ingredients: [
      "картофель", "сливочное масло", "молоко", "соль"
    ],
    isVegeterian: true
  },
  {
    title: "Котлеты",
    ingredients: [
      "говяжий фарш", "сухари панировочные", "яйца", "лук"
    ],
    isVegeterian: false
  }
]

// БЕЗ FILTER
const vegRecipes = [];
for (let i = 0; i < recipes.length; i++) {
  if (recipes[i].isVegeterian) {
    vegRecipes.push(recipes[i]);
  }
}

// C FILTER
const filteredRecipes = recipes.filter((recipe) => recipe.isVegeterian);

Вот так красиво, элегантно, в одну строку. И без изменения исходного массива.

Функция reduce

Эта функция обычно вызывает самые большие затруднения. Многим она кажется сложной, но это не так. Её суть в том, что она позволяет уменьшить массив, свести его к одному значению. Для каждого элемента исходного массива вызывается указанная функция в которую передаются текущий элемент массива и переменная-аккумулятор, в которой мы сохраняем результат выполнения функции, и которая в итоге возвращается как финальный результат.

Синтаксис

array.reduce(function(acc, currentValue, index, arr), initialValue)

Принимаемые аргументы

  • acc – Обязательный аргумент. Переменная-аккумулятор. initialValue при первой итерации и результат выполнения предыдущих итераций при последующих вызовах;
  • currentValue – Обязательный аргумент. Текущий элемент массива;
  • index – Опциональный аргумент. Индекс текущего элемента массива;
  • arr – Опциональный аргумент. Оригинал массива;
  • initialValue – Опциональный аргумент. Начальное значение переменной-аккумулятора. Если не передавать, то по умолчанию используется 0

Ну и пример поможет нам лучше понять это всё. Допустим, у нас есть массив с числами и нам нужно получить сумму всех этих чисел.

const numbers = [10, 20, 30, 40, 50];

const summ = numbers.reduce((acc, number) => acc + number, 0); // 150

Давайте пройдёмся и посмотрим пошагово, что происходит.

  1. В reduce передаётся функция, которая принимает acc(переменная-аккумулятор) и number(текущий элемент массива). В качестве initialValue мы передаём 0;
  2. При вызове для первого элемента acc будет равна 0, текущий элемент — 10. Соответственно, результат acc + number будет равен 10.;
  3. При вызове для следующего элемента acc уже будет равна 10, так как это не первый вызов и мы помним, что в этом случае acc равна результату предыдущего вызова. Соответственно результат acc + number здесь будет 10 + 20 — 30;
  4. Дальше acc уже равна 30 и результат следующей итерации будет 30 + 30 — 60;
  5. Ну тут уже должно быть просто. Здесь acc равна 60 и результат 60 + 40 — 100;
  6. Последний шаг. acc равна 100. Результат 100 + 50 — 150. И так как это последний элемент, то 150 и будет результатом выполнения reduce.

Функция find

Здесь тоже название подсказывает. Данная функция даёт нам возможность найти элемент массива, соответствующий переданным условиям. Возвращается первый найденный элемент. Соответственно, использовать её целесообразно когда вы точно знаете, что нужный вам элемент в массиве в единственном числе.

Синтаксис

array.find(function(currentValue, index, arr), thisValue)

Принимаемые аргументы

  • currentValue – Обязательный аргумент. Текущий элемент массива;
  • index – Опциональный аргумент. Индекс текущего элемента массива;
  • arr – Опциональный аргумент. Оригинал массива;
  • thisValue – Опциональный аргумент. Объект, который нужно использовать как this

В качестве примера рассмотрим уже знакомый нам массив с рецептами. Положим, нам нужно найти рецепт омлета.

const omelet = recipes.find(recipe => recipe.title === "Омлет");

Функция every

Данная функция позволяет нам узнать соответствуют ли все элементы массива заданному условию. Если это так, функция возвращает true. Если хоть один элемент не соответствует, то функция возвращает false.

Синтаксис

array.every(function(currentValue, index, arr), thisValue)

Принимаемые аргументы

  • currentValue – Обязательный аргумент. Текущий элемент массива;
  • index – Опциональный аргумент. Индекс текущего элемента массива;
  • arr – Опциональный аргумент. Оригинал массива;
  • thisValue – Опциональный аргумент. Объект, который нужно использовать как this

Ну и пример. Самый распространённый вариант использования этой функции — проверка, что все поля формы заполнены. Проверим, что все поля имеют хотя бы один символ.

const formData = ['Иван', 'Иванов', 'ivan@mail.ru', ''];

const validateForm = formData.every(field => field.length > 0); // false

Функция some

Данная функция позволяет нам узнать соответствует ли хотя бы один элемент массива заданному условию. Если это так, функция возвращает true. Если ни один элемент не соответствует, то функция возвращает false.

Синтаксис

array.some(function(currentValue, index, arr), thisValue);

Принимаемые аргументы

  • currentValue – Обязательный аргумент. Текущий элемент массива;
  • index – Опциональный аргумент. Индекс текущего элемента массива;
  • arr – Опциональный аргумент. Оригинал массива;
  • thisValue – Опциональный аргумент. Объект, который нужно использовать как this

Допустим, у нас есть подборка рецептов. И мы хотим проверить есть ли там рецепты для вегетарианцев.

const recipes = [
  {
    title: "Омлет",
    ingredients: [
      "яйца", "сливки", "сливочное масло", "соль"
    ],
    isVegeterian: true
  },
  {
    title: "Картофельное пюре",
    ingredients: [
      "картофель", "сливочное масло", "молоко", "соль"
    ],
    isVegeterian: true
  },
  {
    title: "Котлеты",
    ingredients: [
      "говяжий фарш", "сухари панировочные", "яйца", "лук"
    ],
    isVegeterian: false
  }
]

const hasVegeterian = recipes.some(recipe => recipe.isVegeterian);

Ну вот и всё. Перечисленные функции существенно упрощают работу с массивами. Лидерами по полезности, конечно же, являются map, filter и reduce. Я лично уже не представляю как раньше обходились без них.