Массивы и методы для работы с ними: sort, filter, map, reduce, some, every … #
Мы уже рассматривали некоторые, базовые методы для работы с массивами при знакомстве с самими массивами в JavaScript. Настало время изучить ещё несколько очень полезных методов.
Это будет длинная статья. Изначально под каждый метод я хотел сделать отдельную статью, так как это сделано например на MDN.
Но при написании я понял, что у всех методов массива, которые я хотел рассмотреть есть как минимум две общие черты:
- Все методы с удовольствием используют колбэки и соответственно стрелочные функции.
- Если разобраться хотя бы с одним методом, то каждый следующий даётся намного легче.
И некоторая сложность в их понимании возникает скорее у тех, кто по-настоящему не подружился со стрелочными функциями.
Потому для начала убеждаемся, что мы знаем базу - стрелочные функции.
Это статья является базовой в объяснении работы методов массивов. И не заменяет собой документацию. Ссылки на документацию вы найдёте в конце статьи.
Сортировка массива - sort #
Javascript несёт с собой функцию сортировки. Давайте посмотрим её на примере:
let array1 = [2,3,4,5,7,1,6];
console.log(array1.sort()); // [ 1, 2, 3, 4, 5, 6, 7]
Результат работы метода соответствует моим ожиданиям, правда надо помнить, что метод меняет массив и к оригинальному массиву уже не вернуться. Давайте теперь отсортируем города:
let array2 = ["Могилёв","Минск","Москва","Мухино","Муром"];
console.log(array2.sort()); // [ 'Минск', 'Могилёв', 'Москва', 'Муром', 'Мухино' ]
С городами вроде тоже всё хорошо. Может показаться, что метод sort() идеальный метод. Давайте попробуем немного более длинный ряд.
let array3 = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
console.log(array3.sort());
// [ 1, 11, 115, 3, 31, 4, 44, 5, 6, 7]
1, 11 и 115 идут раньше чем три и тридцать один. Сортировка, которую применяет метод sort() является лексикографической. Это значит, что он сортирует по алфавиту. Для него 1 и 11 примерно то же самое, что и “а”, и “аа”. Именно так кстати в некоторых операционных системах сортируют папки. То есть если “Аахен” идёт перед “Арканзасом”, то и “111” идёт перед “22”.
Все символы собраны в “условной” таблице символов. Сегодня это скорее всего юникод. Подробнее почитать можно об этом здесь.
Прежде чем мы научимся сортировать не по алфавиту, а на больше меньше, нам надо понять, что такое больше меньше.
Больше меньше #
И так мы должны написать функцию, которая сравнивая два числа скажет системе, что одно из них больше, а другое меньше.
Давайте начнём, сравнение по-английски compare и наш метод должен принять два числа. Если первое больше, то мы отдадим положительное число, если меньше отрицательное, а если числа равны, то отдадим ноль.
function compare(a, b) {
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
}
Вроде получилось, только нам не обязательно отдавать один и минус один, достаточно просто любое число. И если мы задумаемся, то выясним, что если а больше б, то их разница будет положительной. Если а равно б, то их разница равна нулю. А если а меньше б, то их разница равна отрицательному числу.
Нам всегда надо отдавать разницу, так вернём её сразу.
function compare(a, b) {
return a-b;
}
Теперь мы можем передать этот способ сравнения в функцию sort(). И всё вместе будет выглядеть:
let array = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
function compare(a, b) {
return a - b;
}
array.sort(compare);
console.log(array);
Саму функцию компаре мы конечно же можем переписать как стрелочную функцию и тогда нас кстати меньше смущает отсутствие скобок при передаче её как параметр.
let array = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
let compare = (a, b) => a - b;
array.sort(compare);
console.log(array);
Или даже сразу записать стрелочную функцию как параметр метода sort:
let array = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
array.sort((a, b) => a - b);
console.log(array);
Вот так шаг за шагом мы разобрали ещё один пример использования стрелочной функции, узнали новое слово “лексикографический”, научились сортировать.
Стоит добавить, что стрелочные функции появились в JS s ES6. В ES5 это выглядело ещщё вот так:
// ES5
let array = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
array.sort(function (a, b) {
return a - b;
});
console.log(array); // [ 1, 3, 4, 5, 6, 7, 11, 31, 44, 115]
Помимо более короткой записи у стрелочной функции другой подход к работе со значением this. Но это отдельная тема.
Фильтрация массива - filter #
Первое и самое важное - метод filter возвращает (или создаёт) новый массив. А значит нам нужна переменная, что бы сохранить его. Дайте возьмём список городов и найдём все города в которых больше шести букв.
let cities = ["Могилёв", "Минск", "Москва", "Мухино", "Муром", "Мюнхен", "Мурманск"];
let result = cities.filter(city => city.length > 6);
console.log(result);
//[ 'Могилёв', 'Мурманск' ]
Мы фильтруем массив cities и говорим: “Отфильтруй, пожалуйста, все города, длина которых больше шести”.
То что мы написали вверху, по аналогии с методом сорт можно было бы записать и так:
let cities = ["Могилёв", "Минск", "Москва", "Мухино", "Муром", "Мюнхен", "Мурманск"];
let result = cities.filter(ruleFilter);
function ruleFilter(city) {
return city.length > 6;
}
console.log(result);
//[ 'Могилёв', 'Мурманск' ]
Мне кажется, что постепенно более лаконичная запись должна начать нравиться и вам. Давайте закрепим материал и отсортируем в массиве чисел нужные нам. И это будут все между 5 и 50.
let array = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
let luckyNumbers = array.filter((number) => number > 5 && number < 50);
console.log(luckyNumbers);
// [ 11, 31, 7, 6, 44 ]
Трансформация массива - метод map #
Давайте возведём все числа массива во вторую степень:
let array = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
let luckyNumbers = array.map((number) => number ** 2);
console.log(luckyNumbers);
// [121, 961, 16, 25, 49, 36, 1, 9, 1936, 13225]
Или примем массив городов, а вернём количество символов в названии каждого города:
let cities = ["Могилёв", "Минск", "Москва", "Мухино", "Муром", "Мюнхен", "Мурманск"];
let numbers = cities.map((city) => city.length);
console.log(numbers);
[ 7, 5, 6, 6, 5, 6, 8]
Reduce или самый простой метод в массивах #
Это действительно самый простой метод, потому что он принципиально он объединяет работу двух уже известных нам метода map и filter.
map создаёт новый массив, меняя каждый элемент массива индивидуально. filter создаёт новый массив убирая элементы, которые не соответствуют условиям. reduce в свою очередь, берёт все элементы в массиве, складывает их в новое значение.
Я подготовил пару задач на сегодня и ещё пару для следующей статьи, где мы потрогаем его ещё раз. reduce помогает провести операцию с каждым элементом массива и сохранить одно результирующее значение всей работы.
Например, мы можем найти сумму или среднее арифметическое всех чисел массива. Давайте начнём с суммы:
let array = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
let sum = array.reduce((result, current) => result + current);
console.log(sum);
Я пересчитал на калькуляторе. Всё верно. Колбэк функция передаваемая методу reduce имеет результат и актуальный элемент массива. Начальное значение результата в таком случае будет равно первому элементу массива. Среднее арифметическое можно найти просто поделив сумму на длину массива.
let array = [11, 31, 4, 5, 7, 6, 1, 3, 44, 115];
let sum = array.reduce((result, current) => result + current);
let arithmeticAverage = sum/array.length;
console.log(arithmeticAverage.toFixed(2));
// 22.70
Метод reduce принимает функцию и если хотите начальное значение результата. Сама колбэк функция может принимать помимо результата и актуального элемента его индекс и сам массив.
let output = array.reduce(function(result, currentItem, cirrentIndex, inputArray), resultStartValue => functionLogic);
Ещё несколько примеров рассмотрено по ссылкам в конце урока.
Some and every - методы проверки #
Они возвращают булевое значение если хоть один - some или каждый(every) соответствует условию.
let cities = ["Могилёв", "Минск", "Москва", "Мухино", "Муром", "Мюнхен", "Мурманск"];
let checkSome = cities.some(city => city==="Могилёв");
console.log(checkSome);
let checkEvery = cities.every((city) => city[0] === "М");
console.log(checkEvery);
Домашнее задание #
Прочитать весь материал в дополнительных ссылках