43. map, filter, reduce детально
Добро пожаловать! Сегодня мы разберем три мощных метода массивов в JavaScript: `map`, `filter` и `reduce`. Они позволяют элегантно и эффективно преобразовывать и обрабатывать данные.
## map: Преобразование каждого элемента
Метод `map` создает *новый* массив, применяя функцию к *каждому* элементу исходного массива. Представьте, что у вас есть конвейер, где каждый элемент массива проходит через станцию обработки (вашу функцию) и выходит уже измененным.
```javascriptconst numbers = [1, 2, 3, 4, 5];
// Увеличиваем каждый элемент массива на 2const doubledNumbers = numbers.map(function(number) { return number * 2;});
console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]console.log(numbers); // Output: [1, 2, 3, 4, 5] (Исходный массив не изменился!)Здесь numbers.map(...) применяет анонимную функцию function(number) { return number * 2; } к каждому числу в массиве numbers. Результат - новый массив doubledNumbers с удвоенными значениями. Обратите внимание, что исходный массив numbers остался без изменений.
filter: Отбор нужных элементов
Заголовок раздела «filter: Отбор нужных элементов»Метод filter создает новый массив, содержащий только те элементы исходного массива, для которых функция-условие вернула true. Это как сито, которое пропускает только то, что соответствует критериям.
const numbers = [1, 2, 3, 4, 5, 6];
// Отбираем только четные числаconst evenNumbers = numbers.filter(function(number) { return number % 2 === 0;});
console.log(evenNumbers); // Output: [2, 4, 6]console.log(numbers); // Output: [1, 2, 3, 4, 5, 6] (Исходный массив не изменился!)В этом примере numbers.filter(...) применяет функцию function(number) { return number % 2 === 0; } к каждому числу. Если число четное (остаток от деления на 2 равен 0), то функция возвращает true, и число попадает в новый массив evenNumbers.
reduce: Сведение массива к одному значению
Заголовок раздела «reduce: Сведение массива к одному значению»Метод reduce сводит массив к одному значению (числу, строке, объекту и т.д.), последовательно применяя функцию к каждому элементу и аккумулятору (значению, полученному на предыдущем шаге). Это как калькулятор, который последовательно применяет операцию к числам и накапливает результат.
const numbers = [1, 2, 3, 4, 5];
// Суммируем все элементы массиваconst sum = numbers.reduce(function(accumulator, currentValue) { return accumulator + currentValue;}, 0); // 0 - это начальное значение аккумулятора
console.log(sum); // Output: 15console.log(numbers); // Output: [1, 2, 3, 4, 5] (Исходный массив не изменился!)Здесь numbers.reduce(...) применяет функцию function(accumulator, currentValue) { return accumulator + currentValue; }. accumulator - это текущая сумма (начинается с 0), а currentValue - текущий элемент массива. Функция возвращает сумму accumulator и currentValue, которая становится новым значением accumulator для следующей итерации.
Жизненный пример
Заголовок раздела «Жизненный пример»Представьте, что у вас есть список товаров в интернет-магазине.
- map: Вы можете использовать
mapдля отображения цены каждого товара в определенной валюте. - filter: Вы можете использовать
filterдля отображения только товаров, находящихся в наличии. - reduce: Вы можете использовать
reduceдля вычисления общей стоимости всех товаров в корзине.
В React, Vue и Angular эти методы активно используются для обработки данных, полученных с сервера, для рендеринга списков, фильтрации результатов поиска и агрегации данных. Например, в React:
// Пример в React (псевдокод)const products = [/* ... массив объектов продуктов ... */];
const availableProducts = products.filter(product => product.inStock);const productNames = availableProducts.map(product => product.name);
return ( <ul> {productNames.map(name => <li key={name}>{name}</li>)} </ul>);Ключевые моменты
Заголовок раздела «Ключевые моменты»map,filterиreduceне изменяют исходный массив.mapсоздает новый массив с преобразованными элементами.filterсоздает новый массив с отфильтрованными элементами.reduceсводит массив к одному значению.- Эти методы часто используются вместе для решения сложных задач обработки данных.
- Важно понимать, что каждый метод возвращает.
## Интерактивный пример
<Playground client:load template="static" files={{ "/index.html": `<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <style> body { background: #1e1e2e; color: #cdd6f4; font-family: sans-serif; padding: 20px; margin: 0; } button { background: #89b4fa; color: #1e1e2e; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; margin: 4px; font-size: 14px; } button:hover { background: #74c7ec; } button.active { background: #a6e3a1; } h3 { color: #89b4fa; margin: 16px 0 8px; } .box { background: #313244; border-radius: 8px; padding: 16px; margin: 10px 0; } .step { background: #181825; border-radius: 6px; padding: 12px; margin: 6px 0; font-family: monospace; font-size: 13px; white-space: pre; } .step-label { color: #cba6f7; font-weight: bold; margin-bottom: 4px; } .arr { color: #a6e3a1; } .acc { color: #fab387; } .desc { color: #a6adc8; font-size: 12px; } input { background: #313244; color: #cdd6f4; border: 1px solid #45475a; padding: 6px 10px; border-radius: 4px; font-size: 14px; width: 260px; } label { font-size: 13px; color: #a6adc8; } </style></head><body> <h3>🔄 map → filter → reduce Pipeline</h3> <div class="box"> <label>Числа (через запятую):</label><br> <input id="arr" value="1, 2, 3, 4, 5, 6, 7, 8, 9, 10" style="margin:6px 0"><br> <button onclick="runPipeline()">▶ Запустить pipeline</button> <button onclick="demoChain()">🔗 Цепочка методов</button> <button onclick="demoReduceObj()">📊 reduce → объект</button> </div> <div id="steps"></div> <script> function getArr() { return document.getElementById('arr').value.split(',').map(s => Number(s.trim())).filter(n => !isNaN(n)); } function step(label, desc, value) { return \`<div class="step"><div class="step-label">\${label}</div><div class="desc">\${desc}</div><div class="arr">\${JSON.stringify(value)}</div></div>\`; } window.runPipeline = function() { const arr = getArr(); const doubled = arr.map(n => n * 2); const filtered = doubled.filter(n => n > 10); const sum = filtered.reduce((acc, n) => acc + n, 0); document.getElementById('steps').innerHTML = step('1️⃣ Исходный массив', '', arr) + step('2️⃣ .map(n => n * 2)', 'Умножаем каждый элемент на 2', doubled) + step('3️⃣ .filter(n => n > 10)', 'Оставляем только > 10', filtered) + \`<div class="step"><div class="step-label">4️⃣ .reduce((acc, n) => acc + n, 0)</div><div class="desc">Суммируем все элементы</div>\` + filtered.map((n, i) => \`<div class="acc"> acc=\${filtered.slice(0,i).reduce((a,v)=>a+v,0)} + \${n} = \${filtered.slice(0,i+1).reduce((a,v)=>a+v,0)}</div>\`).join('') + \`<div class="arr">Итог: \${sum}</div></div>\`; } window.demoChain = function() { const arr = getArr(); const r = arr.filter(n => n % 2 === 0).map(n => n * n).reduce((acc, n) => acc + n, 0); document.getElementById('steps').innerHTML = step('Исходный', '', arr) + step('.filter(n => n % 2 === 0)', 'Только чётные', arr.filter(n => n % 2 === 0)) + step('.map(n => n * n)', 'Возводим в квадрат', arr.filter(n => n % 2 === 0).map(n => n*n)) + \`<div class="step"><div class="step-label">.reduce → сумма</div><div class="arr">Итог: \${r}</div></div>\`; } window.demoReduceObj = function() { const arr = getArr(); const grouped = arr.reduce((acc, n) => { const key = n % 2 === 0 ? 'even' : 'odd'; acc[key] = (acc[key] || []); acc[key].push(n); return acc; }, {}); document.getElementById('steps').innerHTML = step('Исходный', '', arr) + \`<div class="step"><div class="step-label">.reduce() → группировка чётных/нечётных</div><div class="arr">\${JSON.stringify(grouped, null, 2)}</div></div>\` + step('.reduce() → частоты', '', arr.reduce((a, n) => { a[n] = (a[n]||0)+1; return a; }, {})); } runPipeline(); </script></body></html>`, }}/>