MakeFirstStep· Frontend + ИИ

Урок 6 · JavaScript

Массивы и их методы

Ассалому алейкум! Откройте любой сайт: список товаров в интернет-магазине, лента сообщений в чате, карточки ресторанов в доставке, расписание автобусов. Всё это — списки, а в JavaScript список называется массивом. Данные с сервера почти всегда приходят массивами, и работа фронтенд-разработчика — превратить такой массив в то, что человек видит на экране: отфильтровать, отсортировать, посчитать итог. Сегодня мы научимся делать это уверенно.

  • Создавать массивы, читать и менять элементы по индексу
  • Добавлять и удалять элементы: push, pop, shift, unshift, splice
  • Копировать части массива через slice и не путать его со splice
  • Понимать колбэки и применять map, filter, find, some, every
  • Считать итоги через reduce и правильно сортировать через sort
  • Собирать методы в цепочки, как настоящие фронтенд-разработчики

01 Что такое массив

До сих пор каждая переменная хранила одно значение: одно имя, одну цену, одну оценку. Но в жизни данные чаще идут списком: список покупок на Шохмансуре, расписание уроков на неделю, оценки всей группы за экзамен. Заводить переменные item1, item2, item3… неудобно: а если покупок будет сто?

Массив — это одна переменная, в которой хранится упорядоченный список значений. «Упорядоченный» значит, что у каждого значения есть своё место: первое, второе, третье и так далее.

Аналогия из жизни

Представьте поезд Душанбе–Худжанд. Поезд — это массив, вагоны — его элементы. Вагоны стоят строго по порядку, и у каждого есть номер. Только в JavaScript нумерация начинается не с 1, а с 0: самый первый вагон — нулевой.

Ещё одна картинка — ячейки камеры хранения на вокзале: ряд одинаковых ячеек, на каждой номер, в каждой что-то лежит. Знаете номер — сразу открываете нужную ячейку.

Массив
упорядоченный список значений в квадратных скобках: ["нон", "чой"].
Элемент
одно значение внутри массива, например "нон".
Индекс
номер места элемента. Считается с нуля: 0, 1, 2…
Длина
сколько элементов в массиве. Узнаём через .length.

Как создать массив

Пишем квадратные скобки, внутри — значения через запятую. Пустые скобки [] — пустой массив, в который потом можно что-то добавить.

Можно редактировать · Ctrl + Enter
const shopping = ["нон", "чой", "шакар", "зардолу"];
const scores = [78, 92, 65];
const empty = [];

console.log(shopping); // → ["нон", "чой", "шакар", "зардолу"]
console.log(scores);   // → [78, 92, 65]
console.log(empty);    // → []
"нон"
0
"чой"
1
"шакар"
2
"зардолу"
3
Массив shopping: сверху значение, снизу индекс. Четыре элемента, индексы от 0 до 3.

Почему индексы начинаются с нуля

Индекс — это не «номер по порядку», а смещение: сколько шагов нужно сделать от начала массива, чтобы дойти до элемента. До первого элемента идти не нужно — ноль шагов, поэтому его индекс 0. До второго — один шаг, индекс 1. Так устроена память компьютера, и почти все языки программирования считают так же.

Отсюда простое правило: индекс последнего элемента = длина − 1. В массиве из 4 элементов последний индекс — 3.

Читаем элементы: индекс, length, at

Можно редактировать · Ctrl + Enter
const wagons = ["локомотив", "вагон-ресторан", "купе", "плацкарт"];

console.log(wagons.length);             // → 4
console.log(wagons[0]);                 // → локомотив
console.log(wagons[2]);                 // → купе
console.log(wagons[wagons.length - 1]); // → плацкарт
console.log(wagons.at(-1));             // → плацкарт
console.log(wagons.at(-2));             // → купе

wagons[wagons.length - 1] — классический способ взять последний элемент: длина 4, значит последний индекс 3. Современный способ короче: wagons.at(-1). Метод at понимает отрицательные числа: −1 — последний, −2 — предпоследний.

Меняем элемент по индексу

Обращаемся к ячейке по индексу и присваиваем новое значение — старое просто заменится.

Можно редактировать · Ctrl + Enter
const schedule = ["HTML", "CSS", "JavaScript"];
schedule[1] = "Figma";

console.log(schedule);     // → ["HTML", "Figma", "JavaScript"]
console.log(schedule[5]);  // → undefined
console.log(schedule[-1]); // → undefined

Если обратиться к индексу, которого нет, JavaScript не выдаст ошибку, а вернёт undefined — «здесь ничего нет». Обратите внимание: schedule[-1] тоже undefined. Отрицательные индексы в квадратных скобках не работают — для этого есть at(-1).

Разные типы в одном массиве

Можно редактировать · Ctrl + Enter
const student = ["Мадина", 19, true, null];
console.log(student);        // → ["Мадина", 19, true, null]
console.log(student.length); // → 4

Так писать можно, но на практике в одном массиве обычно хранят однотипные данные: только цены, только имена, только оценки. С таким массивом проще работать — все элементы можно обработать одинаково.

const-массив всё равно можно менять

Это удивляет почти всех. const запрещает переприсвоить переменную, то есть положить в неё другой массив. Но менять содержимое того же массива можно.

Можно редактировать · Ctrl + Enter
const cart = ["нон"];
cart[1] = "чой";
console.log(cart); // → ["нон", "чой"]
Этот код выдаст ошибку · Ctrl + Enter
const cart = ["нон"];
cart = ["самбуса"]; // пытаемся положить в переменную другой массив
Аналогия из жизни

Вернёмся к камере хранения. Переменная хранит не сами вещи, а ключ от ячейки (программисты говорят «ссылку»). const означает: ключ поменять нельзя, он всегда от этой ячейки. Но открыть ячейку и переложить вещи внутри — пожалуйста.

Перебор массива циклом

Чаще всего с массивом нужно что-то сделать для каждого элемента. Вы уже знаете цикл for — он отлично подходит, когда нужен индекс.

Можно редактировать · Ctrl + Enter
const grades = [5, 4, 5, 3];

for (let i = 0; i < grades.length; i++) {
  console.log(`Оценка №${i + 1}: ${grades[i]}`);
}
// Выведет:
// Оценка №1: 5
// Оценка №2: 4
// Оценка №3: 5
// Оценка №4: 3

Если индекс не нужен, удобнее for...of: на каждом шаге в переменную попадает очередной элемент.

Можно редактировать · Ctrl + Enter
const fruits = ["зардолу", "гранат", "черешня"];

for (const fruit of fruits) {
  console.log(`Купить: ${fruit}`);
}
// Выведет:
// Купить: зардолу
// Купить: гранат
// Купить: черешня

Есть ли элемент в массиве: includes и indexOf

Два маленьких, но очень полезных метода. includes(значение) отвечает «да/нет», а indexOf(значение) возвращает индекс первого совпадения или -1, если ничего не нашлось.

Можно редактировать · Ctrl + Enter
const menu = ["оши палав", "самбуса", "курутоб"];

console.log(menu.includes("самбуса")); // → true
console.log(menu.includes("манту"));   // → false
console.log(menu.indexOf("курутоб"));  // → 2
console.log(menu.indexOf("манту"));    // → -1
Частые ошибки

1. Выход за границу. arr[arr.length] — это уже за последним элементом, получим undefined. Последний — arr[arr.length - 1].

2. Лишний шаг в цикле. Условие i <= arr.length делает на один шаг больше, и последний вывод будет undefined. Правильно: i < arr.length.

3. Скобки после length. length — это свойство, а не функция. arr.length() выдаст ошибку «is not a function». Пишем без скобок: arr.length.

Перед методами: мутирующие и немутирующие

Дальше в уроке будет много методов — встроенных функций, которые есть у каждого массива и вызываются через точку: arr.push(...). Все они делятся на две группы, и это деление надо держать в голове всегда.

Важно

Мутирующие методы меняют исходный массив («мутация» — изменение). После вызова старого массива в прежнем виде больше нет.

Немутирующие методы исходный массив не трогают: они возвращают новое значение, а оригинал остаётся как был. Во фронтенде (например, в React) это особенно важно: данные на экране обновляются правильно только тогда, когда мы не портим исходный массив, а создаём новый.

МетодМеняет исходный?Что возвращает
pushДановую длину массива
popДаудалённый последний элемент
shiftДаудалённый первый элемент
unshiftДановую длину массива
spliceДамассив удалённых элементов
sortДатот же (уже отсортированный) массив
sliceНетновый массив — копию части
mapНетновый массив той же длины
filterНетновый массив подходящих элементов
findНетпервый подходящий элемент или undefined
someНетtrue / false
everyНетtrue / false
reduceНетодно итоговое значение

Возвращайтесь к этой таблице, когда сомневаетесь. Каждый метод мы разберём подробно.

ИИ-подсказка

Если не уверены, что вернёт метод, спросите ИИ коротко и точно: «Что вернёт [1, 2, 3].at(-1) и почему?» — а потом обязательно проверьте ответ кнопкой ▶ Запустить.

Практика: закрепляем

Индексы, длина и циклы — фундамент всего урока. Разберитесь здесь, и дальше будет легко.

ЗАДАЧА 1.1

Список покупок на Шохмансуре

лёгкая

Зарина идёт на базар. Выведите в консоль: первую покупку, последнюю покупку двумя способами (через length и через at) и строку с количеством покупок.

Можно редактировать · Ctrl + Enter
const shopping = ["нон", "чой", "шакар", "зардолу", "гранат"];

// 1. первая покупка
// 2. последняя покупка через length
// 3. последняя покупка через at
// 4. строка "Всего покупок: ..."
Ожидаемый результат
нон
гранат
гранат
Всего покупок: 5
Подсказка 1

Первый элемент всегда под индексом 0. Индекс последнего на единицу меньше длины.

Подсказка 2

Последний: shopping[shopping.length - ___] и shopping.at(___). Для строки используйте шаблон `Всего покупок: ${___}`.

ЗАДАЧА 1.2

Новое расписание

средняя

В группе поменялось расписание: урок под индексом 1 заменили на "Figma". Замените элемент по индексу, а затем циклом for выведите нумерованный список уроков — нумерация для людей начинается с 1.

Можно редактировать · Ctrl + Enter
const lessons = ["HTML", "CSS", "JavaScript", "Git"];

// 1. замените "CSS" на "Figma" по индексу
// 2. выведите список: "1. HTML", "2. ..." циклом for
Ожидаемый результат
1. HTML
2. Figma
3. JavaScript
4. Git
Подсказка 1

Замена — это обычное присваивание: lessons[___] = "Figma".

Подсказка 2

В цикле индекс i идёт от 0, а номер для человека — i + 1. Шаблон строки: `${___}. ${lessons[___]}`.

ЗАДАЧА 1.3

Итоги экзамена

сложная

Учитель внёс баллы группы за экзамен. С помощью цикла for...of посчитайте сумму баллов, средний балл (округлите до одного знака после запятой через toFixed(1)) и сколько студентов набрали 90 и больше. Затем через includes проверьте, есть ли в списке ровно 100 баллов, а через indexOf — на каком индексе стоит результат 54.

Можно редактировать · Ctrl + Enter
const scores = [78, 92, 65, 88, 100, 54, 91];

let sum = 0;
let excellent = 0;

// ваш код здесь
Ожидаемый результат
Сумма: 568
Средний балл: 81.1
Отличников (90+): 3
Есть 100 баллов: true
Позиция 54: 5
Подсказка 1

В цикле на каждом шаге прибавляйте балл к sum, а если балл не меньше 90 — увеличивайте excellent на 1. Средний балл = сумма / количество элементов.

Подсказка 2

Каркас: for (const score of scores) { sum += ___; if (___ >= 90) ___++; }. Средний: (sum / scores.___).toFixed(1).

02 push и pop: добавить и убрать в конце

Самые частые операции со списком — положить что-то в конец и забрать последнее. Пользователь добавил товар в корзину — push. Нажал «Отменить последнее действие» — pop.

Аналогия из жизни

Стопка горячих лепёшек нон в пекарне. Пекарь кладёт новую лепёшку сверху — это push. Покупатель берёт тоже сверху — последнюю положенную. Это pop. Конец массива — это верх стопки.

push — добавить в конец

arr.push(элемент1, элемент2, …)Возвращает: новую длинуМеняет исходный массив
Можно редактировать · Ctrl + Enter
const stack = ["нон", "нон"];
const newLength = stack.push("нон с кунжутом");

console.log(newLength); // → 3
console.log(stack);     // → ["нон", "нон", "нон с кунжутом"]

stack.push("патыр", "фатир"); // можно добавить сразу несколько
console.log(stack.length);    // → 5
"нон"
0
"нон"
1
→ push("нон с кунжутом") →
"нон"
0
"нон"
1
"нон с кунжутом"
2
Новый элемент встаёт в конец и получает индекс, равный старой длине.

pop — забрать последний

arr.pop()Возвращает: удалённый элементМеняет исходный массив
Можно редактировать · Ctrl + Enter
const orders = ["самбуса", "курутоб", "шакароб"];
const last = orders.pop();

console.log(last);   // → шакароб
console.log(orders); // → ["самбуса", "курутоб"]

const empty = [];
console.log(empty.pop()); // → undefined
"самбуса"
0
"курутоб"
1
"шакароб"
2
→ pop() →
"самбуса"
0
"курутоб"
1
pop() ничего не принимает: он всегда убирает последний элемент и отдаёт его нам.

Из пустого массива pop() вернёт undefined — ошибки не будет, просто забирать нечего.

Частая ошибка

Думать, что push возвращает массив. Он возвращает число — новую длину. Поэтому так писать нельзя:

Так плохо

В cart окажется число, а не массив.

Как НЕ надо
let cart = ["нон"];
cart = cart.push("чой");
console.log(cart); // 2 — массив потерян!
Так хорошо

Просто вызываем метод — массив изменится сам.

JavaScript
const cart = ["нон"];
cart.push("чой");
console.log(cart); // ["нон", "чой"]
Совет: shift и unshift — то же самое, но в начале

Если стопка — это push/pop, то очередь — это работа с началом. В очереди в банк или к маршрутке первым обслуживают того, кто пришёл первым: shift() убирает первый элемент и возвращает его. А unshift(элемент) ставит элемент в самое начало (например, пропускаем вперёд пожилого человека) и возвращает новую длину.

arr.shift()Возвращает: удалённый первыйМеняет исходный массив
arr.unshift(элемент)Возвращает: новую длинуМеняет исходный массив
Можно редактировать · Ctrl + Enter
const queue = ["Фарход", "Мадина", "Рустам"];

const first = queue.shift();
console.log(first); // → Фарход
console.log(queue); // → ["Мадина", "Рустам"]

const len = queue.unshift("Абдулло-ака");
console.log(len);   // → 3
console.log(queue); // → ["Абдулло-ака", "Мадина", "Рустам"]

После shift и unshift у всех оставшихся элементов меняются индексы: каждый сдвигается на одно место. Помните об этом, если сохранили индекс в переменную заранее.

МетодГде работаетЧто делаетВозвращает
push(x)конецдобавляетновую длину
pop()конецубираетубранный элемент
unshift(x)началодобавляетновую длину
shift()началоубираетубранный элемент

Практика: закрепляем

Стопка и очередь встречаются в программах постоянно: история действий, список уведомлений, очередь заказов.

ЗАДАЧА 2.1

Стопка лепёшек

лёгкая

Пекарь кладёт в стопку ещё две лепёшки: "нон 3" и "нон 4" — одним вызовом push. Сохраните то, что вернул push, и выведите строку с количеством. Затем покупатель берёт верхнюю лепёшку: заберите её через pop, выведите, что взяли, и саму стопку.

Можно редактировать · Ctrl + Enter
const stack = ["нон 1", "нон 2"];

// ваш код здесь
Ожидаемый результат
Лепёшек в стопке: 4
Взяли: нон 4
["нон 1", "нон 2", "нон 3"]
Подсказка 1

push возвращает новую длину — это и есть количество лепёшек. pop возвращает снятый элемент.

Подсказка 2

const count = stack.push(___, ___); и const taken = stack.___(); — дальше два console.log с шаблонными строками и один со stack.

ЗАДАЧА 2.2

Очередь в банке

средняя

В отделении банка на проспекте Рудаки очередь. Сделайте по порядку: обслужите первого (выведите Обслужили: ...), в конец встаёт Зарина, а вперёд всех пропускаем пожилого Абдулло-ака. В конце выведите очередь и строку с её длиной.

Можно редактировать · Ctrl + Enter
const queue = ["Фарход", "Мадина", "Рустам"];

// ваш код здесь
Ожидаемый результат
Обслужили: Фарход
["Абдулло-ака", "Мадина", "Рустам", "Зарина"]
В очереди: 4
Подсказка 1

Первого убирает shift, в конец добавляет push, в начало — unshift.

Подсказка 2

Порядок важен: сначала const served = queue.shift(), потом queue.push(___), потом queue.unshift(___). Длина — queue.___.

ЗАДАЧА 2.3

Кнопка «Отменить»

сложная

В интернет-магазине хранится история действий пользователя. Напишите функцию undo(history): она убирает последнее действие и возвращает строку Отменено: .... Если история пуста, функция возвращает Нечего отменять. Вызовите её 4 раза и выведите результаты, а в конце выведите саму историю.

Можно редактировать · Ctrl + Enter
const history = ["ввёл имя", "выбрал город", "нажал «Купить»"];

function undo(list) {
  // ваш код здесь
}

console.log(undo(history));
console.log(undo(history));
console.log(undo(history));
console.log(undo(history));
console.log(history);
Ожидаемый результат
Отменено: нажал «Купить»
Отменено: выбрал город
Отменено: ввёл имя
Нечего отменять
[]
Подсказка 1

Сначала проверьте, пуст ли массив: у пустого массива длина равна 0. Только если есть что отменять — вызывайте pop.

Подсказка 2

if (list.length === ___) return "Нечего отменять"; затем const action = list.___(); и return `Отменено: ${___}`;

03 splice: удалить, вставить, заменить в середине

push и pop работают только с концом. А что если нужно убрать остановку из середины маршрута или вставить новый урок между двумя другими? Для этого есть универсальный метод splice — «швейцарский нож» массивов.

Аналогия из жизни

Поезд стоит на станции. Рабочий может в любом месте состава отцепить несколько вагонов и на их место прицепить новые. Нужно сказать ему три вещи: с какого вагона начать, сколько отцепить и какие вагоны прицепить вместо них. Ровно эти три вещи принимает splice.

arr.splice(start, deleteCount, ...items)Возвращает: массив удалённыхМеняет исходный массив
start
индекс, с которого начинаем. Может быть отрицательным: −1 — последний элемент.
deleteCount
сколько элементов удалить, начиная со start. Если 0 — ничего не удаляем.
...items
необязательные элементы, которые вставятся на место start. Три точки означают «сколько угодно штук через запятую».

1. Удалить

Можно редактировать · Ctrl + Enter
const stops = ["Вокзал", "Зелёный базар", "Шохмансур", "Цирк", "Корвон"];
const removed = stops.splice(1, 2);

console.log(removed); // → ["Зелёный базар", "Шохмансур"]
console.log(stops);   // → ["Вокзал", "Цирк", "Корвон"]
  1. Встаём на индекс 1 — это «Зелёный базар».
  2. Удаляем 2 элемента: «Зелёный базар» и «Шохмансур».
  3. Вставлять нечего — третьего аргумента нет. Оставшиеся элементы сдвигаются влево.
  4. Возвращается массив удалённых — его можно сохранить или просто не использовать.
"Вокзал"
0
"Зелёный базар"
1
"Шохмансур"
2
"Цирк"
3
"Корвон"
4
→ splice(1, 2) →
"Вокзал"
0
"Цирк"
1
"Корвон"
2
После удаления «Цирк» и «Корвон» получили новые индексы.

2. Вставить (deleteCount = 0)

Можно редактировать · Ctrl + Enter
const lessons = ["HTML", "CSS", "JavaScript"];
lessons.splice(2, 0, "Figma", "Git");

console.log(lessons); // → ["HTML", "CSS", "Figma", "Git", "JavaScript"]

Встаём на индекс 2, удаляем 0 элементов и вставляем два новых. Всё, что стояло с индекса 2 и дальше, сдвигается вправо.

3. Заменить (удалить и вставить одновременно)

Можно редактировать · Ctrl + Enter
const menu = ["самбуса", "манту", "курутоб"];
const old = menu.splice(1, 1, "оши палав");

console.log(old);  // → ["манту"]
console.log(menu); // → ["самбуса", "оши палав", "курутоб"]

Отрицательный start

Можно редактировать · Ctrl + Enter
const fruits = ["зардолу", "гранат", "дыня", "виноград"];
fruits.splice(-2, 1); // встаём на второй с конца — "дыня"

console.log(fruits); // → ["зардолу", "гранат", "виноград"]
Ловушка: забыли deleteCount

Если указать только start, splice удалит всё от start до конца.

Можно редактировать · Ctrl + Enter
const cities = ["Душанбе", "Худжанд", "Бохтар", "Куляб"];
const cut = cities.splice(1);

console.log(cities); // → ["Душанбе"]
console.log(cut);    // → ["Худжанд", "Бохтар", "Куляб"]
Частая ошибка

Ждать, что splice вернёт новый массив без удалённых элементов. Нет: он возвращает удалённые, а изменённым становится сам исходный массив. Если написать const result = arr.splice(0, 1), в result будет массив из одного удалённого элемента.

Практика: закрепляем

Главное в splice — чётко ответить на три вопроса: откуда, сколько удалить, что вставить.

ЗАДАЧА 3.1

Остановка на ремонте

лёгкая

Остановку «Цирк» закрыли на ремонт, и маршрутка её пропускает. Удалите её из маршрута через splice. Выведите массив удалённых и обновлённый маршрут.

Можно редактировать · Ctrl + Enter
const stops = ["Вокзал", "Зелёный базар", "Шохмансур", "Цирк", "Корвон"];

// ваш код здесь
Ожидаемый результат
["Цирк"]
["Вокзал", "Зелёный базар", "Шохмансур", "Корвон"]
Подсказка 1

Сначала найдите индекс «Цирка» — посчитайте вручную или через indexOf.

Подсказка 2

const removed = stops.splice(___, 1); — удаляем один элемент.

ЗАДАЧА 3.2

Рецепт плова

средняя

Рустам записал продукты для оши палав, но забыл мясо. Двумя вызовами splice: 1) вставьте "мясо" сразу после "морковь", ничего не удаляя; 2) замените "масло" на "зира". Выведите массив и его длину.

Можно редактировать · Ctrl + Enter
const plov = ["рис", "морковь", "лук", "масло"];

// ваш код здесь
Ожидаемый результат
["рис", "морковь", "мясо", "лук", "зира"]
5
Подсказка 1

Вставка — это deleteCount = 0. Замена — deleteCount = 1 плюс новый элемент. После вставки индекс «масла» изменился — проверьте его заново.

Подсказка 2

plov.splice(2, ___, "мясо"); затем plov.splice(plov.indexOf("масло"), ___, ___);

ЗАДАЧА 3.3

Перестановка в маршруте

сложная

Шахром планирует поездку по городам и хочет менять их порядок. Напишите функцию moveItem(arr, from, to), которая вынимает элемент с индекса from и вставляет его на индекс to (меняя исходный массив). Используйте только splice — два раза.

Можно редактировать · Ctrl + Enter
const route = ["Душанбе", "Хорог", "Куляб", "Бохтар"];

function moveItem(arr, from, to) {
  // ваш код здесь
}

moveItem(route, 1, 3);
console.log(route);
moveItem(route, 2, 0);
console.log(route);
Ожидаемый результат
["Душанбе", "Куляб", "Бохтар", "Хорог"]
["Бохтар", "Душанбе", "Куляб", "Хорог"]
Подсказка 1

Первый splice удаляет один элемент и возвращает массив из него. Сам элемент — это нулевой элемент этого массива.

Подсказка 2

const item = arr.splice(from, 1)[___]; потом arr.splice(to, ___, item);

04 slice: скопировать часть массива

slice («ломтик, кусочек») берёт часть массива и возвращает её как новый массив. Исходный массив при этом не меняется совсем. Это один из самых безопасных методов.

Аналогия из жизни

Вам нужны три страницы из учебника. Вы не вырываете их, а делаете ксерокопию. У вас в руках новые листы, а учебник остался целым. slice — это ксерокопия, а splice из прошлой темы — это ножницы.

arr.slice(start, end)Возвращает: новый массивНе меняет исходный

Конец не включается

Самое важное правило: элемент с индексом end в результат не попадает. slice(1, 4) берёт индексы 1, 2 и 3 — «от 1 до 4, но 4 не трогаем».

"Пн"
0
"Вт"
1
"Ср"
2
"Чт"
3
"Пт"
4
"Сб"
5
days.slice(1, 4) → ["Вт", "Ср", "Чт"]. Индекс 4 — это «стоп-линия», сам он не берётся. Длина результата = end − start = 3.
Можно редактировать · Ctrl + Enter
const days = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб"];

console.log(days.slice(1, 4)); // → ["Вт", "Ср", "Чт"]
console.log(days.slice(3));    // → ["Чт", "Пт", "Сб"]
console.log(days.slice(-2));   // → ["Пт", "Сб"]
console.log(days.slice(0, -1)); // → ["Пн", "Вт", "Ср", "Чт", "Пт"]
console.log(days);             // → ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб"]
  1. slice(3) — без end берём всё от индекса 3 до конца.
  2. slice(-2) — отрицательный индекс считается с конца: последние два элемента.
  3. slice(0, -1) — всё, кроме последнего: стоп-линия на последнем элементе.
  4. Последняя строка показывает, что days остался прежним.

Полная копия: slice() без аргументов

Иногда нужен полный дубликат массива, чтобы менять его, не трогая оригинал. Для этого — slice() без аргументов или запись [...arr] (три точки «раскладывают» элементы в новые скобки).

Можно редактировать · Ctrl + Enter
const original = ["нон", "чой"];
const copy1 = original.slice();
const copy2 = [...original];

copy1.push("самбуса");
copy2.push("курутоб");

console.log(original); // → ["нон", "чой"]
console.log(copy1);    // → ["нон", "чой", "самбуса"]
console.log(copy2);    // → ["нон", "чой", "курутоб"]
Частая ошибка: это не копия

const copy = original; не создаёт новый массив. Вы просто сделали второй ключ от той же ячейки камеры хранения. Меняете copy — меняется и original.

Можно редактировать · Ctrl + Enter
const original = ["Фарход", "Мадина"];
const notCopy = original; // это та же самая ссылка
notCopy.push("Рустам");

console.log(original); // → ["Фарход", "Мадина", "Рустам"]

splice или slice? Классическая путаница

Названия отличаются одной буквой, а поведение — полностью. Запомните: в splice есть «p» — как в слове «попортить» исходник.

splice(start, deleteCount, ...items)slice(start, end)
Исходный массивменяетне трогает
Второй аргументсколько удалитьдо какого индекса (не включая)
Возвращаетмассив удалённыхновый массив-копию части
Может вставлятьданет
Аналогияножницыксерокопия
Можно редактировать · Ctrl + Enter
const a = [10, 20, 30, 40, 50];
const b = [10, 20, 30, 40, 50];

console.log(a.slice(1, 3));  // → [20, 30]
console.log(b.splice(1, 3)); // → [20, 30, 40]
console.log(a);              // → [10, 20, 30, 40, 50]
console.log(b);              // → [10, 50]

Практика: закрепляем

slice часто нужен во фронтенде: показать первые 5 товаров, разбить список на страницы, сделать безопасную копию.

ЗАДАЧА 4.1

Жаркая неделя в Душанбе

лёгкая

Даны дневные температуры за неделю июля. Через slice получите температуры первых трёх дней и последних двух дней (через отрицательный индекс). В конце выведите исходный массив, чтобы убедиться, что он не изменился.

Можно редактировать · Ctrl + Enter
const temps = [38, 40, 41, 39, 37, 36, 42];

// ваш код здесь
Ожидаемый результат
[38, 40, 41]
[36, 42]
[38, 40, 41, 39, 37, 36, 42]
Подсказка 1

Первые три дня — индексы 0, 1, 2. Значит стоп-линия на индексе 3.

Подсказка 2

temps.slice(0, ___) и temps.slice(___) с отрицательным числом.

ЗАДАЧА 4.2

Страницы меню

средняя

На сайте чайханы меню показывают по 3 блюда на странице. Напишите функцию getPage(list, page), которая возвращает блюда нужной страницы (страницы нумеруются с 1). Выведите страницы 1, 2, 3 и 4.

Можно редактировать · Ctrl + Enter
const menu = ["самбуса", "курутоб", "оши палав", "шакароб", "манту", "лагман", "чой", "нон"];
const perPage = 3;

function getPage(list, page) {
  // ваш код здесь
}

console.log(getPage(menu, 1));
console.log(getPage(menu, 2));
console.log(getPage(menu, 3));
console.log(getPage(menu, 4));
Ожидаемый результат
["самбуса", "курутоб", "оши палав"]
["шакароб", "манту", "лагман"]
["чой", "нон"]
[]
Подсказка 1

Страница 1 — индексы 0–2, страница 2 — индексы 3–5. Найдите, как начало страницы зависит от её номера. slice не ругается, если end больше длины.

Подсказка 2

Начало: (page - 1) * ___, конец: page * ___. Верните list.slice(start, end).

ЗАДАЧА 4.3

«…и ещё 2»

сложная

Под постом в соцсети показывают не всех, кто поставил лайк, а несколько первых и надпись «…и ещё N». Напишите функцию shorten(list, n): она возвращает новый массив из первых n имён, а если имён больше, чем n, добавляет в конец строку …и ещё X. Исходный массив меняться не должен.

Можно редактировать · Ctrl + Enter
const likes = ["Фарход", "Мадина", "Рустам", "Зарина", "Шахром"];

function shorten(list, n) {
  // ваш код здесь
}

console.log(shorten(likes, 3));
console.log(shorten(likes, 10));
console.log(likes);
Ожидаемый результат
["Фарход", "Мадина", "Рустам", "…и ещё 2"]
["Фарход", "Мадина", "Рустам", "Зарина", "Шахром"]
["Фарход", "Мадина", "Рустам", "Зарина", "Шахром"]
Подсказка 1

Сначала сделайте копию первых n элементов через slice. В эту копию можно смело делать push — оригинал не пострадает.

Подсказка 2

const result = list.slice(0, ___); Сколько осталось: list.length - ___. Если это число больше нуля — result.push(`…и ещё ${___}`).

05 Колбэк и map: преобразовать каждый элемент

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

Что такое колбэк

Колбэк (от английского callback — «обратный вызов») — это функция, которую мы отдаём методу, а он сам вызывает её для каждого элемента. Мы не пишем цикл — цикл уже спрятан внутри метода. Мы только объясняем, что сделать с одним элементом.

Аналогия из жизни

Вы отдаёте ткачихе на фабрике в Худжанде мешок хлопка и инструкцию: «из каждого клубка сделай нитку». Вы не стоите рядом и не командуете каждым движением — она сама берёт клубки по одному и для каждого выполняет вашу инструкцию. Инструкция — это колбэк, ткачиха — это метод.

Чтобы колбэк не казался магией, напишем свой маленький «метод» на знакомом цикле. Именно так map устроен внутри:

Можно редактировать · Ctrl + Enter
function myMap(list, callback) {
  const result = [];
  for (const item of list) {
    result.push(callback(item)); // вызываем ВАШУ функцию для каждого элемента
  }
  return result;
}

const double = (n) => n * 2;
console.log(myMap([4, 7, 10], double)); // → [8, 14, 20]

Заметьте: мы передали double без скобок. Со скобками double() мы бы вызвали функцию сами и отдали её результат. А нам нужно отдать саму функцию, чтобы её вызывал метод.

map — встроенный способ

arr.map((element, index, array) => новоеЗначение)Возвращает: новый массив той же длиныНе меняет исходный

map («отобразить, сопоставить») проходит по каждому элементу, вызывает колбэк и складывает то, что колбэк вернул, в новый массив. Сколько элементов было — столько и останется.

Можно редактировать · Ctrl + Enter
const pricesKg = [18, 25, 12];
const pricesTwoKg = pricesKg.map((price) => price * 2);

console.log(pricesTwoKg); // → [36, 50, 24]
console.log(pricesKg);    // → [18, 25, 12]

Как это выполняется, шаг за шагом

ШагЭлемент priceКолбэк вычисляетРезультат кладётся в новый массив
11818 * 2[36]
22525 * 2[36, 50]
31212 * 2[36, 50, 24]

Параметры колбэка: element, index, array

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

Можно редактировать · Ctrl + Enter
const guests = ["Нилуфар", "Далер", "Сабрина"];
const numbered = guests.map((name, index) => `${index + 1}. ${name}`);

console.log(numbered); // → ["1. Нилуфар", "2. Далер", "3. Сабрина"]
Можно редактировать · Ctrl + Enter
const cities = ["душанбе", "худжанд", "хорог"];
const titles = cities.map((city) => city[0].toUpperCase() + city.slice(1));

console.log(titles); // → ["Душанбе", "Худжанд", "Хорог"]

Во втором примере колбэк берёт первую букву (city[0]), делает её заглавной (toUpperCase()) и приклеивает остаток строки (slice(1) работает и у строк). Это обычная фронтенд-задача: привести данные к красивому виду перед показом.

Частая ошибка: забыли return в фигурных скобках

Стрелочная функция без фигурных скобок возвращает результат сама. А с фигурными скобками нужно писать return явно — иначе функция вернёт undefined, и весь новый массив будет из undefined.

Можно редактировать · Ctrl + Enter
const prices = [10, 20, 30];

const wrong = prices.map((p) => { p * 2 });
console.log(wrong); // → [undefined, undefined, undefined]

const right = prices.map((p) => { return p * 2; });
console.log(right); // → [20, 40, 60]

const short = prices.map((p) => p * 2);
console.log(short); // → [20, 40, 60]
Частая ошибка: map ради побочных действий

map нужен, когда вам нужен новый массив. Если вы просто хотите вывести каждый элемент в консоль и результат не используете, берите for...of (или метод forEach — он делает то же самое, но ничего не возвращает).

Так плохо

Создаётся ненужный массив из undefined.

Как НЕ надо
names.map((name) => console.log(name));
Так хорошо

Цикл честно говорит: «просто пройдись по списку».

JavaScript
for (const name of names) {
  console.log(name);
}
ИИ-подсказка

Если колбэк кажется непонятным, попросите ИИ: «Распиши в таблице, что происходит на каждом шаге [3, 5, 8].map(n => n * 10): какой элемент приходит и что возвращается». Пошаговая таблица снимает почти все вопросы.

Практика: закрепляем

Правило для map: на входе N элементов — на выходе N элементов, каждый преобразован колбэком.

ЗАДАЧА 5.1

Зардолу на развес

лёгкая

Абрикосы на базаре стоят 18 сомони за килограмм. Покупатели взяли разное количество килограммов. Через map получите массив стоимостей каждой покупки.

Можно редактировать · Ctrl + Enter
const kg = [2, 5, 1, 3];
const pricePerKg = 18;

// const costs = ...
// console.log(costs);
Ожидаемый результат
[36, 90, 18, 54]
Подсказка 1

Колбэк получает количество килограммов и должен вернуть стоимость.

Подсказка 2

const costs = kg.map((weight) => ___ * ___);

ЗАДАЧА 5.2

Список для страницы

средняя

Во фронтенде map постоянно превращает данные в разметку. Превратите массив имён в массив строк вида <li>1. Нилуфар</li> — с номером, который начинается с 1.

Можно редактировать · Ctrl + Enter
const guests = ["Нилуфар", "Далер", "Сабрина"];

// const items = ...
// console.log(items);
Ожидаемый результат
["<li>1. Нилуфар</li>", "<li>2. Далер</li>", "<li>3. Сабрина</li>"]
Подсказка 1

Номер берётся из второго параметра колбэка — индекса. Не забудьте прибавить 1.

Подсказка 2

guests.map((name, i) => `<li>${___}. ${___}</li>`)

ЗАДАЧА 5.3

Заработок на сборе хлопка

сложная

Далер собирал хлопок 5 дней. За каждый килограмм платят 1.5 сомони, а если за день собрано 110 кг и больше — ещё бонус 20 сомони. Через map получите массив заработков по дням и выведите его. Затем вторым map получите строки День 1: 200 сомони и выведите каждую отдельной строкой через for...of.

Можно редактировать · Ctrl + Enter
const harvest = [120, 95, 140, 80, 110];

// ваш код здесь
Ожидаемый результат
[200, 142.5, 230, 120, 185]
День 1: 200 сомони
День 2: 142.5 сомони
День 3: 230 сомони
День 4: 120 сомони
День 5: 185 сомони
Подсказка 1

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

Подсказка 2

(kg) => { let pay = kg * ___; if (kg >= ___) pay += ___; return pay; }. Для строк используйте индекс: (pay, i) => `День ${i + 1}: ...`.

06 filter: оставить только подходящие

filter («фильтр, сито») проходит по массиву и оставляет только те элементы, для которых колбэк ответил true. Во фронтенде это поиск, фильтры в каталоге, вкладки «Все / Непрочитанные».

Аналогия из жизни

Вы перебираете черешню перед продажей: каждую ягоду смотрите и решаете — «в ящик» или «нет». Ваше решение «да/нет» — это колбэк. Ящик с отобранными ягодами — новый массив. Исходная куча черешни никуда не делась.

arr.filter((element, index, array) => true или false)Возвращает: новый массив (может быть пустым)Не меняет исходный
Можно редактировать · Ctrl + Enter
const prices = [12, 45, 8, 120, 30];
const cheap = prices.filter((price) => price < 40);

console.log(cheap);  // → [12, 8, 30]
console.log(prices); // → [12, 45, 8, 120, 30]
Элементprice < 40Попадает в результат?
12trueда
45falseнет
8trueда
120falseнет
30trueда

В отличие от map, длина результата может быть любой: от нуля до длины исходного массива. Сами элементы filter не меняет — только решает, оставить их или нет.

Пустой результат и подсчёт через length

Можно редактировать · Ctrl + Enter
const scores = [55, 72, 91, 64, 98];

const failed = scores.filter((s) => s < 50);
console.log(failed);        // → []
console.log(failed.length); // → 0

const top = scores.filter((s) => s >= 90).length;
console.log(`Отличников: ${top}`); // → Отличников: 2

Если никто не подошёл, получаем пустой массив [], а не ошибку. А связка filter(...).length — самый простой способ посчитать, сколько элементов подходит под условие.

Фильтруем строки

Можно редактировать · Ctrl + Enter
const cities = ["Душанбе", "Хорог", "Худжанд", "Куляб", "Бохтар"];

const long = cities.filter((city) => city.length > 5);
console.log(long); // → ["Душанбе", "Худжанд", "Бохтар"]

const onH = cities.filter((city) => city.startsWith("Х"));
console.log(onH);  // → ["Хорог", "Худжанд"]

startsWith("Х") — метод строки: «начинается ли строка с этой буквы?». Возвращает true или false, поэтому отлично подходит для колбэка фильтра.

Мини-превью: объект

В реальных данных товар — это не просто цена, а набор сведений: название, цена, наличие. Для этого в JavaScript есть объект — фигурные скобки с парами «ключ: значение», например { name: "Нон", price: 4 }. Чтобы достать значение, пишут имя объекта, точку и ключ: если item — это такой объект, то item.price равно 4, а item.name — "Нон".

Подробно объекты мы разберём в следующем уроке, а пока этого достаточно, чтобы читать примеры ниже.

Можно редактировать · Ctrl + Enter
const products = [
  { name: "Нон", price: 4 },
  { name: "Чой кабуд", price: 25 },
  { name: "Самбуса", price: 6 },
];

const affordable = products.filter((item) => item.price <= 10);
console.log(affordable); // → [{ name: "Нон", price: 4 }, { name: "Самбуса", price: 6 }]
Частые ошибки

1. Забыли return в фигурных скобках. Колбэк вернёт undefined, а это считается «нет» — результат будет пустым [].

2. Ждут один элемент, а получают массив. filter всегда возвращает массив, даже если подошёл один элемент: ["Хорог"], а не "Хорог". Если нужен один элемент — есть find, о нём в следующей теме.

Практика: закрепляем

Колбэк фильтра — это вопрос, на который можно ответить «да» или «нет».

ЗАДАЧА 6.1

Голы «Истиклола»

лёгкая

Даны голы «Истиклола» в последних матчах. Получите массив матчей, где команда забила 2 гола и больше, и выведите, сколько таких матчей.

Можно редактировать · Ctrl + Enter
const goals = [3, 0, 2, 1, 4, 0, 2];

// ваш код здесь
Ожидаемый результат
[3, 2, 4, 2]
Результативных матчей: 4
Подсказка 1

Условие «2 и больше» записывается как >= 2.

Подсказка 2

const good = goals.filter((g) => ___); количество — good.___.

ЗАДАЧА 6.2

Города для экскурсии

средняя

Есть список городов Таджикистана. Получите два массива: 1) города, в названии которых больше 7 букв; 2) города, которые начинаются на букву «Х». Выведите оба.

Можно редактировать · Ctrl + Enter
const cities = ["Душанбе", "Худжанд", "Бохтар", "Куляб", "Хорог", "Истаравшан", "Пенджикент", "Турсунзаде"];

// ваш код здесь
Ожидаемый результат
["Истаравшан", "Пенджикент", "Турсунзаде"]
["Худжанд", "Хорог"]
Подсказка 1

Длина строки — city.length, как у массива. «Больше 7» — это > 7, а не >= 7.

Подсказка 2

Для второго массива подойдёт city.startsWith(___) или проверка первой буквы city[0] === ___.

ЗАДАЧА 6.3

Убрать повторы

сложная

В корзину приложения доставки попали повторяющиеся названия. Через filter получите массив без повторов (каждое название — один раз, в порядке первого появления). Затем выведите, сколько повторов было удалено.

Можно редактировать · Ctrl + Enter
const cart = ["нон", "чой", "нон", "самбуса", "чой", "нон"];

// ваш код здесь
Ожидаемый результат
["нон", "чой", "самбуса"]
Удалено повторов: 3
Подсказка 1

Элемент встречается впервые, если indexOf этого элемента (он ищет первое совпадение) равен текущему индексу. Индекс приходит вторым параметром колбэка.

Подсказка 2

cart.filter((item, i) => cart.indexOf(___) === ___). Повторов удалено: разница длин двух массивов.

07 find и findIndex: найти первый подходящий

Часто нужен не список, а один конкретный элемент: товар с нужным названием, студент с нужным именем, первый жаркий день. Для этого есть find.

Аналогия из жизни

Вы ищете на стоянке такси машину, которая едет в Варзоб. Вы идёте вдоль ряда и спрашиваете водителей по очереди. Как только нашли первого подходящего — садитесь и дальше не спрашиваете. А если никто не едет — уходите ни с чем. Это и есть find: первый найденный или undefined.

arr.find((element, index, array) => true или false)Возвращает: первый подходящий элемент или undefinedНе меняет исходный
Можно редактировать · Ctrl + Enter
const temps = [36, 38, 41, 43, 39];

console.log(temps.find((t) => t > 40)); // → 41
console.log(temps.find((t) => t > 50)); // → undefined

Подходят и 41, и 43, но find останавливается на первом совпадении и возвращает сам элемент — не массив.

findIndex — где он стоит

arr.findIndex((element) => true или false)Возвращает: индекс или -1Не меняет исходный
Можно редактировать · Ctrl + Enter
const temps = [36, 38, 41, 43, 39];

const i = temps.findIndex((t) => t > 40);
console.log(i);                                  // → 2
console.log(`Жара началась в день ${i + 1}`);    // → Жара началась в день 3
console.log(temps.findIndex((t) => t > 50));     // → -1

Чем findIndex отличается от indexOf? indexOf ищет конкретное значение (indexOf(41)), а findIndex — элемент по условию («первый больше 40»).

find с объектами

Помните мини-превью объектов? С ними find раскрывается полностью: находим запись по имени и берём из неё нужное значение.

Можно редактировать · Ctrl + Enter
const students = [
  { name: "Фарход", score: 74 },
  { name: "Зарина", score: 95 },
  { name: "Далер", score: 58 },
];

const zarina = students.find((s) => s.name === "Зарина");
console.log(zarina);       // → { name: "Зарина", score: 95 }
console.log(zarina.score); // → 95

find или filter?

findfilter
Что возвращаетодин элементмассив элементов
Если ничего не нашлосьundefined[]
Когда останавливаетсяна первом совпадениипроходит весь массив
Пример задачи«товар с id 7»«все товары дешевле 50»
Частая ошибка: не проверили undefined

Если find ничего не нашёл, он вернёт undefined. Попытка взять у него свойство сломает программу.

Этот код выдаст ошибку · Ctrl + Enter
const students = [{ name: "Фарход", score: 74 }];
const found = students.find((s) => s.name === "Манижа");
console.log(found.score); // у undefined нет свойства score
Можно редактировать · Ctrl + Enter
const students = [{ name: "Фарход", score: 74 }];
const found = students.find((s) => s.name === "Манижа");

if (found) {
  console.log(found.score);
} else {
  console.log("Студент не найден"); // → Студент не найден
}

Практика: закрепляем

Всегда держите в голове вариант «не нашлось» — в реальных данных он случается постоянно.

ЗАДАЧА 7.1

Первый дождливый день

лёгкая

Даны осадки в Душанбе по дням недели (в миллиметрах). Найдите через find первое значение больше 5, а через findIndex — его индекс. Выведите строку с номером дня для человека (с 1). Затем поищите значение больше 100 и выведите результат.

Можно редактировать · Ctrl + Enter
const rain = [0, 2, 0, 7, 12, 0, 3];

// ваш код здесь
Ожидаемый результат
7
Сильный дождь в день 4
undefined
Подсказка 1

Оба метода принимают одинаковый колбэк (mm) => mm > 5, но возвращают разное: значение и индекс.

Подсказка 2

const day = rain.findIndex(___); номер для человека — day + ___.

ЗАДАЧА 7.2

Журнал группы

средняя

Есть журнал с баллами студентов. Найдите и выведите: 1) балл Нилуфар; 2) имя первого студента с баллом ниже 60; 3) индекс Шахрома в журнале.

Можно редактировать · Ctrl + Enter
const students = [
  { name: "Фарход", score: 74 },
  { name: "Нилуфар", score: 88 },
  { name: "Шахром", score: 52 },
  { name: "Манижа", score: 45 },
];

// ваш код здесь
Ожидаемый результат
88
Шахром
2
Подсказка 1

find вернёт целый объект студента — значение берите через точку: .score или .name.

Подсказка 2

students.find((s) => s.name === ___).score; для индекса — students.findIndex((s) => ___).

ЗАДАЧА 7.3

Запись к врачу

сложная

Сайт поликлиники показывает ближайшее свободное время. Напишите функцию firstFree(slots, busy): она возвращает первое время из slots, которого нет в busy. Если свободного времени нет, функция возвращает строку Свободных мест нет.

Можно редактировать · Ctrl + Enter
const slots = ["09:00", "09:30", "10:00", "10:30", "11:00"];
const busyMonday = ["09:00", "09:30", "10:30"];
const busyTuesday = ["09:00", "09:30", "10:00", "10:30", "11:00"];

function firstFree(slots, busy) {
  // ваш код здесь
}

console.log(firstFree(slots, busyMonday));
console.log(firstFree(slots, busyTuesday));
console.log(firstFree(slots, []));
Ожидаемый результат
10:00
Свободных мест нет
09:00
Подсказка 1

Время свободно, если оно не входит в массив busy. Для проверки «входит ли» у вас есть includes, а «не» — это !.

Подсказка 2

const free = slots.find((time) => !busy.___(time)); Потом: если free === undefined — верните строку-сообщение, иначе — free.

08 some: есть ли хотя бы один?

Иногда не нужно ничего искать или отбирать — нужен просто ответ «да» или «нет». Есть ли в корзине товар без наличия? Есть ли среди оценок двойка? Есть ли непрочитанные сообщения? Для этого есть some («некоторые, хотя бы один»).

Аналогия из жизни

Учитель заходит в класс и спрашивает: «Хотя бы у одного есть зарядка для телефона?». Как только поднялась первая рука — ответ «да», остальных можно не спрашивать. Если не поднял никто — ответ «нет».

arr.some((element, index, array) => true или false)Возвращает: true / falseНе меняет исходный
Можно редактировать · Ctrl + Enter
const grades = [4, 5, 3, 5, 2];

console.log(grades.some((g) => g === 2)); // → true
console.log(grades.some((g) => g === 1)); // → false

some останавливается рано

Убедимся, что some действительно прекращает проверку на первом «да». Добавим в колбэк console.log:

Можно редактировать · Ctrl + Enter
const prices = [15, 40, 250, 30, 600];

const hasExpensive = prices.some((p) => {
  console.log(`Проверяю ${p}`);
  return p > 200;
});
console.log(hasExpensive);
// Выведет:
// Проверяю 15
// Проверяю 40
// Проверяю 250
// true

До 30 и 600 очередь не дошла: ответ уже известен. На больших массивах это экономит время.

Пустой массив → false

Можно редактировать · Ctrl + Enter
const messages = [];
console.log(messages.some((m) => m === "новое")); // → false

Логично: в пустом массиве нет ни одного элемента, значит нет и «хотя бы одного подходящего».

Совет: some и includes

Если вы ищете конкретное значение, проще includes: grades.includes(2). А some нужен, когда есть условие: «есть ли оценка ниже трёх», «есть ли товар дороже 200».

Частая ошибка

Использовать filter(...).length > 0 там, где нужен просто ответ «да/нет». Работать будет, но filter пройдёт весь массив и создаст ненужный новый массив. some короче, понятнее и останавливается раньше.

Практика: закрепляем

some — это вопрос «хотя бы один?». Результат всегда true или false.

ЗАДАЧА 8.1

Есть ли несдавшие?

лёгкая

Даны баллы за контрольную. Проверьте через some: есть ли хотя бы один результат 90 и выше, и есть ли хотя бы один результат ниже 50. Выведите строки.

Можно редактировать · Ctrl + Enter
const scores = [55, 62, 48, 70];

// ваш код здесь
Ожидаемый результат
Есть отличники: false
Есть несдавшие: true
Подсказка 1

Нужно два отдельных вызова some с разными условиями.

Подсказка 2

scores.some((s) => s >= ___) и scores.some((s) => s < ___), результат подставьте в шаблонную строку.

ЗАДАЧА 8.2

Проверка на аллергию

средняя

В приложении ресторана гость указал продукты, на которые у него аллергия. Напишите функцию hasAllergen(dish, allergens): она возвращает true, если хотя бы один продукт блюда есть в списке аллергенов. Проверьте самбусу и шакароб.

Можно редактировать · Ctrl + Enter
const allergens = ["орехи", "кунжут"];
const sambusa = ["тесто", "мясо", "лук", "кунжут"];
const shakarob = ["помидор", "огурец", "лук", "кашнич", "фатир"];

function hasAllergen(dish, allergens) {
  // ваш код здесь
}

console.log(`Самбуса опасна: ${hasAllergen(sambusa, allergens)}`);
console.log(`Шакароб опасен: ${hasAllergen(shakarob, allergens)}`);
Ожидаемый результат
Самбуса опасна: true
Шакароб опасен: false
Подсказка 1

Идём по продуктам блюда и для каждого спрашиваем: «этот продукт входит в аллергены?».

Подсказка 2

return dish.some((product) => allergens.___(product));

ЗАДАЧА 8.3

Проверка формы

сложная

Перед отправкой формы регистрации нужно убедиться, что все поля заполнены. Напишите функцию checkForm(fields): если хотя бы одно поле пустое — вернуть Заполните все поля, иначе — Форма отправлена. Важно: поле, в котором только пробелы, тоже считается пустым.

Можно редактировать · Ctrl + Enter
const form1 = ["Мадина", "", "madina@mail.tj", "Душанбе"];
const form2 = ["Рустам", "   ", "rustam@mail.tj", "Бохтар"];
const form3 = ["Сабрина", "+992 90 123 45 67", "sabrina@mail.tj", "Куляб"];

function checkForm(fields) {
  // ваш код здесь
}

console.log(checkForm(form1));
console.log(checkForm(form2));
console.log(checkForm(form3));
Ожидаемый результат
Заполните все поля
Заполните все поля
Форма отправлена
Подсказка 1

У строк есть метод trim() — он убирает пробелы по краям: " ".trim() превращается в "".

Подсказка 2

const hasEmpty = fields.some((f) => f.___() === ___); затем if и два return.

09 every: все ли подходят?

every («каждый») — брат some. Он отвечает на вопрос: «все ли элементы подходят под условие?». Все ли товары в наличии? Все ли студенты сдали? Все ли поля формы правильные?

Аналогия из жизни

Водитель маршрутки перед поездкой на Искандеркуль проверяет: все ли пассажиры пристегнулись? Он идёт по салону, и как только видит первого непристёгнутого — ответ «нет», можно дальше не смотреть. Ответ «да» будет, только если пристёгнуты все до одного.

arr.every((element, index, array) => true или false)Возвращает: true / falseНе меняет исходный
Можно редактировать · Ctrl + Enter
const scores = [72, 85, 60, 91];

console.log(scores.every((s) => s >= 60)); // → true
console.log(scores.every((s) => s >= 70)); // → false

every останавливается на первом «нет», так же как some останавливается на первом «да». Во втором вызове проверка закончилась на 60.

Пустой массив → true. Почему?

Можно редактировать · Ctrl + Enter
const cart = [];
console.log(cart.every((qty) => qty > 0)); // → true

Это кажется странным, но логика такая: every ищет нарушителя — элемент, который не подходит. В пустом массиве нарушителей нет, значит «все подходят». Как у водителя маршрутки: если в салоне никого нет, то никто и не сидит непристёгнутым.

Ловушка

Из-за этого правила every на пустом массиве легко обманывает. Проверка «все товары в корзине в наличии» на пустой корзине даст true — и кнопка «Оформить заказ» станет активной. Если пустой массив для вас — особый случай, проверьте его отдельно: arr.length === 0.

some против every

someevery
Вопросхотя бы один подходит?все подходят?
Останавливаетсяна первом trueна первом false
Пустой массивfalsetrue
Похож на оператор|| (ИЛИ)&& (И)

Связь с логикой из прошлых уроков прямая. [a, b, c].some(check) — это как check(a) || check(b) || check(c). А [a, b, c].every(check) — как check(a) && check(b) && check(c).

Можно редактировать · Ctrl + Enter
const ages = [19, 22, 17];
const isAdult = (age) => age >= 18;

console.log(ages.some(isAdult));  // → true
console.log(ages.every(isAdult)); // → false
console.log(isAdult(19) && isAdult(22) && isAdult(17)); // → false

Обратите внимание: колбэк можно объявить заранее и передать по имени — ages.some(isAdult). Без скобок, как мы обсуждали в теме про колбэки.

Практика: закрепляем

Перед каждым every спросите себя: что должно случиться, если массив пустой?

ЗАДАЧА 9.1

Сдали все?

лёгкая

Экзамен считается сданным при 60 баллах и выше. Проверьте через every две группы и выведите строки.

Можно редактировать · Ctrl + Enter
const groupA = [72, 85, 60, 91];
const groupB = [72, 59, 88];

// ваш код здесь
Ожидаемый результат
Группа A сдала: true
Группа B сдала: false
Подсказка 1

Условие одно и то же для обеих групп — можно вынести его в отдельную стрелочную функцию.

Подсказка 2

const passed = (s) => s >= ___; затем groupA.every(___).

ЗАДАЧА 9.2

Погода для лагеря

средняя

Летний лагерь подходит для детей, если каждый день температура от 18 до 30 градусов включительно. Напишите функцию isComfortable(temps) и проверьте Варзоб, Душанбе и пустой прогноз. Подумайте, почему для пустого прогноза получается такой ответ.

Можно редактировать · Ctrl + Enter
const varzob = [24, 27, 22, 29, 26];
const dushanbe = [35, 38, 29, 40];
const noData = [];

function isComfortable(temps) {
  // ваш код здесь
}

console.log(isComfortable(varzob));
console.log(isComfortable(dushanbe));
console.log(isComfortable(noData));
Ожидаемый результат
true
false
true
Подсказка 1

«От 18 до 30 включительно» — это два условия сразу, соединённые через И.

Подсказка 2

return temps.every((t) => t >= ___ && t <= ___);

ЗАДАЧА 9.3

Можно ли оформить заказ?

сложная

В корзине хранится количество каждого товара. Напишите функцию canCheckout(quantities), которая возвращает: Корзина пуста — если товаров нет; Есть товар с нулевым количеством — если хотя бы у одного товара количество 0; Можно оформлять — если все количества больше нуля. Помните про ловушку пустого массива.

Можно редактировать · Ctrl + Enter
function canCheckout(quantities) {
  // ваш код здесь
}

console.log(canCheckout([2, 1, 3]));
console.log(canCheckout([2, 0, 1]));
console.log(canCheckout([]));
Ожидаемый результат
Можно оформлять
Есть товар с нулевым количеством
Корзина пуста
Подсказка 1

Порядок проверок важен. Пустой массив проверьте самым первым: иначе every скажет true, и функция ответит «Можно оформлять».

Подсказка 2

if (quantities.length === 0) return ___; затем if (quantities.some((q) => q === 0)) return ___; и последняя проверка через every.

10 reduce: свести массив к одному значению

reduce («свести, сократить») — самый мощный и самый непривычный метод урока. Он проходит по массиву и собирает из всех элементов одно значение: сумму, максимум, итоговую строку, подсчёт. Не торопитесь: разберём его медленно.

Аналогия из жизни

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

Число на экране кассира называется аккумулятор (от слова «накапливать»). Это как копилка: в неё всё время что-то добавляется, и в конце мы её открываем.

arr.reduce((acc, current) => новыйAcc, начальноеЗначение)Возвращает: одно итоговое значениеНе меняет исходный
acc
аккумулятор — промежуточный итог. На первом шаге равен начальному значению, дальше — тому, что колбэк вернул на прошлом шаге.
current
текущий элемент массива.
начальное значение
второй аргумент reduce (после колбэка). С чего начинает кассир: обычно 0 для суммы.

Считаем чек шаг за шагом

Можно редактировать · Ctrl + Enter
const bill = [120, 45, 30, 75];

const total = bill.reduce((acc, current) => acc + current, 0);
console.log(total); // → 270

Колбэк вызывается 4 раза — по разу на каждый элемент. Каждый раз он получает старый итог и текущую цену, а возвращает новый итог.

Шагacc (было на экране)current (блюдо)Колбэк возвращает acc + current
стартacc = 0 — начальное значение, второй аргумент reduce
101200 + 120 = 120
212045120 + 45 = 165
316530165 + 30 = 195
419575195 + 75 = 270
итогэлементы закончились — reduce возвращает последний acc: 270

Обратите внимание на главное: то, что колбэк вернул, становится acc на следующем шаге. Если колбэк ничего не вернёт (забыли return в фигурных скобках), на следующем шаге acc будет undefined.

Тот же код, но с «подглядыванием» в каждый шаг:

Можно редактировать · Ctrl + Enter
const bill = [120, 45, 30, 75];

const total = bill.reduce((acc, current) => {
  console.log(`acc = ${acc}, current = ${current}, вернём ${acc + current}`);
  return acc + current;
}, 0);
console.log(`Итого: ${total} сомони`);
// Выведет:
// acc = 0, current = 120, вернём 120
// acc = 120, current = 45, вернём 165
// acc = 165, current = 30, вернём 195
// acc = 195, current = 75, вернём 270
// Итого: 270 сомони

Всегда передавайте начальное значение

Начальное значение можно не писать — тогда acc на старте станет первым элементом массива, а обход начнётся со второго. Для суммы чисел это даже сработает. Но есть два случая, где это ломает программу.

Случай 1: пустой массив. Без начального значения reduce не знает, с чего начать, и выдаёт ошибку.

Этот код выдаст ошибку · Ctrl + Enter
const emptyBill = [];
const total = emptyBill.reduce((acc, current) => acc + current);
console.log(total);
Можно редактировать · Ctrl + Enter
const emptyBill = [];
const total = emptyBill.reduce((acc, current) => acc + current, 0);
console.log(total); // → 0

Случай 2: элементы не того типа, что итог. Посчитаем общую длину имён. Без начального значения первым acc станет строка «Фарход», и дальше числа будут приклеиваться к строке.

Можно редактировать · Ctrl + Enter
const names = ["Фарход", "Мадина", "Рустам"];

const wrong = names.reduce((acc, name) => acc + name.length);
console.log(wrong); // → Фарход66

const right = names.reduce((acc, name) => acc + name.length, 0);
console.log(right); // → 18
Совет

Сделайте привычкой: всегда пишите начальное значение. Для суммы — 0, для строки — "", для подсчёта — {}. Это сразу показывает, какого типа будет результат.

Максимум

Можно редактировать · Ctrl + Enter
const temps = [36, 41, 38, 43, 39];
const max = temps.reduce((acc, t) => (t > acc ? t : acc), temps[0]);

console.log(max); // → 43

Здесь в acc хранится «самое большое на данный момент». На каждом шаге сравниваем: если текущий больше — он становится новым максимумом, иначе оставляем старый. Начинаем с первого элемента, а не с 0: вдруг все температуры отрицательные (зимой в Хороге бывает и −20).

Для простого максимума есть и короткий путь — Math.max(...temps). Но разобрать его через reduce полезно: так же решаются задачи, где готовой функции нет.

Подсчёт повторений (лёгкое знакомство с объектом)

Начальное значение может быть пустым объектом {}. На каждом шаге добавляем в него «счётчик» для текущего слова.

Можно редактировать · Ctrl + Enter
const orders = ["чой", "самбуса", "чой", "нон", "чой"];

const count = orders.reduce((acc, item) => {
  acc[item] = (acc[item] || 0) + 1;
  return acc;
}, {});
console.log(count); // → { чой: 3, самбуса: 1, нон: 1 }

Строка acc[item] = (acc[item] || 0) + 1 читается так: «возьми текущий счёт этого блюда, а если его ещё нет — возьми 0, прибавь 1 и запиши обратно». Квадратные скобки acc[item] — это способ обратиться к ключу, имя которого лежит в переменной. Подробнее — в уроке про объекты.

Склеиваем строку

Можно редактировать · Ctrl + Enter
const fruits = ["зардолу", "гранат", "дыня"];

const text = fruits.reduce((acc, fruit, i) => acc + (i === 0 ? "" : ", ") + fruit, "");
console.log(text); // → зардолу, гранат, дыня

console.log(fruits.join(", ")); // → зардолу, гранат, дыня

Для склейки есть готовый метод join(разделитель) — он возвращает строку из всех элементов. Используйте его, когда нужно просто склеить. А reduce пригодится, когда склейка сложнее: с условиями, с нумерацией, с подсчётом по ходу.

Частые ошибки

1. Забыли return в колбэке с фигурными скобками — на втором шаге acc станет undefined, а итог — NaN («не число»).

2. Перепутали порядок параметров. Первым всегда идёт аккумулятор, вторым — текущий элемент: (acc, current).

3. Не передали начальное значение — смотрите два случая выше.

ИИ-подсказка

reduce лучше всего понимается через таблицу. Попросите: «Сделай таблицу шагов для моего reduce: колонки acc, current, что вернул колбэк» — и вставьте свой код и данные.

Практика: закрепляем

Перед тем как писать reduce, ответьте на два вопроса: с чего начинаем (начальное значение) и как из старого итога и текущего элемента получить новый итог.

ЗАДАЧА 10.1

Счёт в чайхоне

лёгкая

Компания друзей заказала чай, самбусу, плов и лепёшки. Посчитайте итог чека через reduce и выведите строку.

Можно редактировать · Ctrl + Enter
const bill = [15, 8, 25, 6, 12];

// ваш код здесь
Ожидаемый результат
Итого: 66 сомони
Подсказка 1

Новый итог = старый итог + текущая цена. Начинаем с нуля.

Подсказка 2

const total = bill.reduce((acc, price) => ___ + ___, ___);

ЗАДАЧА 10.2

Рекорд бригады

средняя

Даны килограммы хлопка, собранные каждым членом бригады за день. Через reduce (без Math.max и Math.min) найдите самый большой и самый маленький результат.

Можно редактировать · Ctrl + Enter
const harvest = [140, 95, 182, 120, 176];

// ваш код здесь
Ожидаемый результат
Рекорд: 182 кг
Меньше всех: 95 кг
Подсказка 1

В acc храните «лучший результат на данный момент». Начальное значение — первый элемент массива.

Подсказка 2

harvest.reduce((acc, kg) => (kg > acc ? ___ : ___), harvest[0]) — для минимума поменяйте знак сравнения.

ЗАДАЧА 10.3

Голосование: куда поедем?

сложная

Группа голосует, куда поехать в выходные. Через reduce с начальным значением {} посчитайте голоса за каждое место и выведите получившийся объект. Затем выведите строку с количеством голосов за Варзоб, взяв его из этого объекта.

Можно редактировать · Ctrl + Enter
const votes = ["Варзоб", "Искандеркуль", "Варзоб", "Ирам", "Искандеркуль", "Варзоб"];

// ваш код здесь
Ожидаемый результат
{ Варзоб: 3, Искандеркуль: 2, Ирам: 1 }
За Варзоб: 3
Подсказка 1

Вспомните пример с заказами в чайхоне выше: на каждом шаге увеличивайте счётчик для текущего места и возвращайте acc.

Подсказка 2

В колбэке: acc[place] = (acc[place] || ___) + ___; return ___;. Голоса за Варзоб: result["Варзоб"].

11 sort: сортировка

Сортировка нужна везде: товары «сначала дешёвые», студенты по алфавиту, матчи по дате. Метод sort упорядочивает элементы массива. Но у него есть две особенности, на которых спотыкаются почти все. Разберём обе.

arr.sort((a, b) => число)Возвращает: тот же массив, уже отсортированныйМеняет исходный массив

Особенность 1: без колбэка sort сравнивает как строки

Можно редактировать · Ctrl + Enter
const prices = [5, 100, 25, 1];
prices.sort();

console.log(prices); // → [1, 100, 25, 5]

Почему 100 оказалось раньше 25? Без колбэка sort превращает каждый элемент в строку и сравнивает строки по символам, как слова в словаре: "1", "100", "25", "5". Строка "100" начинается с «1», поэтому стоит раньше "25", которая начинается с «2». Как в словаре «ананас» стоит раньше «банан», независимо от длины.

Колбэк-сравнитель: (a, b) => a - b

Чтобы сортировать числа правильно, передаём функцию сравнения (компаратор). sort сам много раз берёт пары элементов a и b и спрашивает колбэк: «кто из них должен стоять раньше?». Колбэк отвечает числом:

Колбэк вернулЧто это значит для sort
отрицательное число (например, −3)a ставим раньше b
положительное число (например, 7)b ставим раньше a
0порядок между ними не важен, оставляем как есть

Теперь смотрите, почему a - b даёт сортировку по возрастанию. Пусть a = 5, b = 25: 5 − 25 = −20, отрицательное — значит 5 раньше 25. Пусть a = 100, b = 25: 100 − 25 = 75, положительное — значит 25 раньше 100. Меньшие числа уходят вперёд. А b - a делает всё наоборот — по убыванию.

Можно редактировать · Ctrl + Enter
const prices = [5, 100, 25, 1];

prices.sort((a, b) => a - b);
console.log(prices); // → [1, 5, 25, 100]

prices.sort((a, b) => b - a);
console.log(prices); // → [100, 25, 5, 1]
Совет: как запомнить

a - b — «а потом б», по алфавиту, по возрастанию: от меньшего к большему. b - a — наоборот, от большего к меньшему.

Сортировка строк: localeCompare

Для строк вычитание не работает ("Абдулло" - "Далер" — это NaN). Можно вызвать sort() без колбэка, но для русских имён он ошибается: заглавные буквы у него идут раньше строчных, а буква «Ё» вообще улетает в начало.

Можно редактировать · Ctrl + Enter
const names = ["манижа", "Фарход", "Абдулло"];
console.log([...names].sort()); // → ["Абдулло", "Фарход", "манижа"]
console.log([...names].sort((a, b) => a.localeCompare(b))); // → ["Абдулло", "манижа", "Фарход"]

const team = ["Жасур", "Ёдгор", "Евгений"];
console.log([...team].sort()); // → ["Ёдгор", "Евгений", "Жасур"]
console.log([...team].sort((a, b) => a.localeCompare(b))); // → ["Евгений", "Ёдгор", "Жасур"]

a.localeCompare(b) — метод строки, который сравнивает её с другой строкой «по-человечески», с учётом правил языка. Он сам возвращает отрицательное число, положительное или 0 — ровно то, что нужно sort. Для сортировки «от Я до А» поменяйте местами: b.localeCompare(a).

Сортировка объектов

Колбэк получает два объекта, и мы сравниваем их нужное поле.

Можно редактировать · Ctrl + Enter
const products = [
  { name: "Курутоб", price: 35 },
  { name: "Самбуса", price: 6 },
  { name: "Оши палав", price: 30 },
];

products.sort((a, b) => a.price - b.price);
console.log(products[0].name);    // → Самбуса
console.log(products.at(-1).name); // → Курутоб

Особенность 2: sort меняет исходный массив

sort не создаёт новый массив — он переставляет элементы прямо в исходном. Если оригинальный порядок ещё нужен (например, порядок, в котором данные пришли с сервера), сначала сделайте копию.

Можно редактировать · Ctrl + Enter
const scores = [70, 95, 82];

const sorted1 = [...scores].sort((a, b) => b - a);
const sorted2 = scores.slice().sort((a, b) => b - a);
const sorted3 = scores.toSorted((a, b) => b - a);

console.log(sorted1); // → [95, 82, 70]
console.log(sorted2); // → [95, 82, 70]
console.log(sorted3); // → [95, 82, 70]
console.log(scores);  // → [70, 95, 82]

Третий способ — современный метод toSorted. Он делает то же, что sort, но возвращает новый массив и не трогает оригинал. Он появился в JavaScript недавно (2023 год) и работает во всех современных браузерах.

Частые ошибки

1. Сортировка чисел без колбэка: [5, 100, 25].sort() → [100, 25, 5]. Для чисел всегда пишите (a, b) => a - b.

2. Колбэк возвращает true/false вместо числа: (a, b) => a > b. Такой колбэк работает непредсказуемо. Нужно именно число: a - b.

3. Забыли, что sort мутирует, и «испортили» исходные данные. Сначала копия — потом сортировка.

Практика: закрепляем

Главный вопрос в каждой задаче: числа или строки, по возрастанию или по убыванию, и нужен ли оригинал.

ЗАДАЧА 11.1

Расстояния от Душанбе

лёгкая

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

Можно редактировать · Ctrl + Enter
const km = [341, 203, 525, 100, 62];

// ваш код здесь
Ожидаемый результат
[62, 100, 203, 341, 525]
[525, 341, 203, 100, 62]
Подсказка 1

Без колбэка вы получите [100, 203, 341, 525, 62] — числа сравнились как строки, и «62» оказалось последним.

Подсказка 2

km.sort((a, b) => ___ - ___), для убывания поменяйте a и b местами.

ЗАДАЧА 11.2

Список группы по алфавиту

средняя

Учителю нужен список группы по алфавиту, но исходный порядок (порядок записи на курс) должен сохраниться. Получите отсортированную копию через localeCompare, затем выведите копию и оригинал.

Можно редактировать · Ctrl + Enter
const group = ["Шахром", "Абдулло", "Манижа", "Далер", "Зарина", "Фарход"];

// ваш код здесь
Ожидаемый результат
["Абдулло", "Далер", "Зарина", "Манижа", "Фарход", "Шахром"]
["Шахром", "Абдулло", "Манижа", "Далер", "Зарина", "Фарход"]
Подсказка 1

Сначала копия ([...group], slice() или сразу toSorted), потом сортировка.

Подсказка 2

const sorted = group.toSorted((a, b) => a.___(b));

ЗАДАЧА 11.3

Бомбардиры «Истиклола»

сложная

Отсортируйте бомбардиров по количеству голов по убыванию. Если голов поровну — по имени по алфавиту. Затем выведите таблицу вида 1. Далер — 12 через for...of или map с индексом.

Можно редактировать · Ctrl + Enter
const players = [
  { name: "Шахром", goals: 7 },
  { name: "Далер", goals: 12 },
  { name: "Рустам", goals: 7 },
  { name: "Абдулло", goals: 9 },
  { name: "Фарход", goals: 3 },
];

// ваш код здесь
Ожидаемый результат
1. Далер — 12
2. Абдулло — 9
3. Рустам — 7
4. Шахром — 7
5. Фарход — 3
Подсказка 1

В колбэке сначала проверьте: если голы разные — верните разницу голов (по убыванию). Если одинаковые — сравните имена.

Подсказка 2

(a, b) => { if (a.goals !== b.goals) return ___ - ___; return a.name.___(b.name); }

12 Цепочки методов

Вы уже заметили: map, filter и slice возвращают новый массив. А у нового массива тоже есть все методы. Значит, можно сразу вызвать следующий метод через точку — получится цепочка. Так выглядит почти любой реальный код обработки данных на фронтенде.

Аналогия из жизни

Конвейер на заводе соков в Истаравшане. На первом участке отбраковывают гнилые абрикосы (filter), на втором их моют и режут (map), на третьем всё сливают в одну бочку сока (reduce). Каждый участок получает результат предыдущего. Читаем цепочку так же — сверху вниз, участок за участком.

Задача: в магазине на Корвоне взять товары дороже 100 сомони, сделать на них скидку 10% и посчитать сумму. Сначала по шагам, с промежуточными переменными:

Можно редактировать · Ctrl + Enter
const prices = [450, 60, 380, 15, 220];

const expensive = prices.filter((p) => p > 100);
console.log(expensive);  // → [450, 380, 220]

const discounted = expensive.map((p) => p * 0.9);
console.log(discounted); // → [405, 342, 198]

const total = discounted.reduce((acc, p) => acc + p, 0);
console.log(total);      // → 945

А теперь то же самое одной цепочкой. Каждый метод пишем с новой строки, начиная с точки, — так цепочку легко читать:

Можно редактировать · Ctrl + Enter
const prices = [450, 60, 380, 15, 220];

const total = prices
  .filter((p) => p > 100)       // [450, 380, 220]
  .map((p) => p * 0.9)          // [405, 342, 198]
  .reduce((acc, p) => acc + p, 0); // 945

console.log(`К оплате: ${total} сомони`); // → К оплате: 945 сомони
  1. prices — исходные данные, 5 цен.
  2. .filter(...) — остаются 3 дорогих товара. Возвращается новый массив.
  3. .map(...) — к новому массиву применяется скидка. Снова новый массив той же длины.
  4. .reduce(...) — массив сворачивается в одно число. Цепочка заканчивается: у числа уже нет методов массива.

Цепочка с объектами и сортировкой

Можно редактировать · Ctrl + Enter
const students = [
  { name: "Рустам", score: 54 },
  { name: "Мадина", score: 95 },
  { name: "Фарход", score: 88 },
  { name: "Нилуфар", score: 43 },
];

const honorBoard = students
  .filter((s) => s.score >= 60)
  .sort((a, b) => b.score - a.score)
  .map((s) => `${s.name} (${s.score})`)
  .join(", ");

console.log(honorBoard); // → Мадина (95), Фарход (88)

Здесь sort стоит после filter — и это безопасно: filter уже вернул новый массив, поэтому сортировка меняет его, а не исходный students.

Частые ошибки в цепочках

1. reduce в середине. После reduce обычно получается число или строка, и вызвать на нём .map нельзя — будет ошибка «is not a function».

2. sort в начале цепочки испортит исходный массив. Ставьте его после filter/map/slice или используйте toSorted.

3. Слишком длинная цепочка, которая не работает. Не гадайте — разбейте её на промежуточные переменные и выведите каждую через console.log, как в первом примере. Так вы сразу увидите, на каком участке конвейера проблема.

Практика: закрепляем

Это задачи, похожие на настоящую работу фронтенд-разработчика. Сначала разбейте задачу на участки конвейера словами, потом пишите код.

ЗАДАЧА 12.1

Распродажа на Корвоне

лёгкая

Магазин одежды объявил скидку 10% на всё дороже 100 сомони. Цепочкой методов получите массив цен со скидкой (только для дорогих товаров) и выведите его. Затем посчитайте итоговую сумму к оплате за эти товары.

Можно редактировать · Ctrl + Enter
const items = [
  { name: "Куртка", price: 450 },
  { name: "Шапка", price: 60 },
  { name: "Кроссовки", price: 380 },
  { name: "Носки", price: 15 },
  { name: "Рюкзак", price: 220 },
];

// ваш код здесь
Ожидаемый результат
[405, 342, 198]
К оплате: 945 сомони
Подсказка 1

Участки конвейера: оставить дорогие → превратить каждый товар в цену со скидкой → сложить.

Подсказка 2

items.filter((it) => it.price > ___).map((it) => it.price * ___), затем .reduce((acc, p) => ___, 0) на полученном массиве.

ЗАДАЧА 12.2

Сдавшие экзамен

средняя

Экзамен сдан при 60 баллах и выше. Получите массив имён сдавших по алфавиту, а затем средний балл только среди сдавших.

Можно редактировать · Ctrl + Enter
const students = [
  { name: "Фарход", score: 88 },
  { name: "Мадина", score: 95 },
  { name: "Рустам", score: 54 },
  { name: "Зарина", score: 76 },
  { name: "Шахром", score: 61 },
  { name: "Нилуфар", score: 43 },
  { name: "Далер", score: 90 },
];

// ваш код здесь
Ожидаемый результат
["Далер", "Зарина", "Мадина", "Фарход", "Шахром"]
Средний балл сдавших: 82
Подсказка 1

Сначала сохраните сдавших в переменную через filter — она пригодится для обеих частей задачи.

Подсказка 2

Имена: passed.map((s) => ___).sort((a, b) => a.localeCompare(b)). Средний: сумма баллов через reduce, делённая на passed.___.

ЗАДАЧА 12.3

Погодный отчёт по Душанбе

сложная

Есть прогноз на неделю. Напишите функцию weekReport(days), которая выводит 4 строки: самый жаркий день (через reduce), среднюю температуру (с одним знаком после запятой), список дней жарче 38° через запятую и ответ, был ли хотя бы один день прохладнее 30°.

Можно редактировать · Ctrl + Enter
const week = [
  { day: "Пн", temp: 34 },
  { day: "Вт", temp: 37 },
  { day: "Ср", temp: 40 },
  { day: "Чт", temp: 41 },
  { day: "Пт", temp: 38 },
  { day: "Сб", temp: 35 },
  { day: "Вс", temp: 39 },
];

function weekReport(days) {
  // ваш код здесь
}

weekReport(week);
Ожидаемый результат
Самый жаркий: Чт (41°)
Средняя: 37.7°
Жарче 38°: Ср, Чт, Вс
Был день прохладнее 30°: false
Подсказка 1

Каждая строка — свой метод: жаркий день — reduce, где acc — весь объект дня; средняя — reduce для суммы и toFixed(1); список — filter + map + join; последний вопрос — some.

Подсказка 2

Жаркий: days.reduce((acc, d) => (d.temp > acc.temp ? ___ : ___), days[0]). Список: days.filter((d) => d.temp > ___).map((d) => d.___).join(", "). Помните: 38 — это не «жарче 38».

✓ Итоги: шпаргалка по массивам

  • [] — массив; индексы с 0; последний — arr[arr.length - 1] или arr.at(-1); несуществующий индекс → undefined.
  • const-массив можно менять внутри, но нельзя переприсвоить.
  • includes(x) → есть ли значение; indexOf(x) → индекс или -1.
  • push(x) / pop() — конец массива; unshift(x) / shift() — начало. Мутируют.
  • splice(start, deleteCount, ...items) — удалить/вставить/заменить в середине; возвращает удалённые. Мутирует.
  • slice(start, end) — копия части, end не включается; slice() и [...arr] — полная копия.
  • Колбэк — функция, которую метод сам вызывает для каждого элемента: (element, index, array).
  • map — преобразовать каждый, длина та же. Не забывайте return в фигурных скобках.
  • filter — оставить подходящие, результат может быть [].
  • find / findIndex — первый подходящий или undefined / -1. Проверяйте «не нашлось».
  • some — хотя бы один? (пустой → false); every — все? (пустой → true).
  • reduce((acc, cur) => ..., начало) — свести к одному значению. Начальное значение — всегда.
  • sort((a, b) => a - b) — числа по возрастанию; строки — a.localeCompare(b). Мутирует: копируйте или берите toSorted.
  • join(", ") — склеить массив в строку.
  • Цепочка: filter → map → reduce, читаем сверху вниз, как конвейер.
Как просить ИИ про массивы

1. Называйте метод. Если вы знаете, какой метод нужен, скажите это: «реши через filter и map, без циклов». Иначе ИИ может выдать решение с непонятными вам приёмами.

2. Давайте пример данных и ожидаемый результат. «Вот массив [450, 60, 380], хочу получить [405, 342]» — так ИИ точно поймёт задачу и не будет гадать.

3. Просите объяснить каждый шаг. Не просто «напиши код», а «объясни, что происходит на каждом шаге, и покажи промежуточные значения». Код, который вы понимаете, — ваш; код, который просто скопировали, — нет.

сделай чтобы цены считались
У меня массив товаров: [{ name: "Куртка", price: 450 }, { name: "Шапка", price: 60 }]. Хочу получить сумму цен товаров дороже 100 сомони со скидкой 10%. Реши цепочкой filter → map → reduce, без циклов. После кода объясни каждый шаг и покажи, какой массив получается после каждого метода.