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

Методы массивов в JavaScript #

Методы массивов — встроенные функции для перебора, преобразования и агрегации без ручных циклов с индексами. map, filter и reduce — основа обработки корзины, рейтингов и плейлистов в современном JS; важно понимать, какие методы мутируют исходный массив, а какие возвращают новый.


forEach: побочные эффекты #

forEach ничего не возвращает (undefined); подходит для логирования или вызова API:

const cart = [
  { sku: 'kb-01', qty: 1 },
  { sku: 'ms-02', qty: 3 },
]

cart.forEach((line) => {
  console.log(line.sku, line.qty)
})

Если нужен новый массив — map, не forEach с push.


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

const prices = [490, 1290, 199]

const withCurrency = prices.map((p) => `${p} ₽`)
// ["490 ₽", "1290 ₽", "199 ₽"]

const doubledQty = cart.map((line) => ({
  ...line,
  qty: line.qty * 2,
}))

map всегда возвращает массив той же длины, что и исходный.


filter: оставить подходящие #

const inStock = cart.filter((line) => line.qty > 0)

const premium = scores.filter((entry) => entry.points >= 1000)

Пустой результат — валидный массив [], не null.


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

const cart = [
  { price: 490, qty: 2 },
  { price: 199, qty: 1 },
]

const subtotal = cart.reduce((sum, line) => sum + line.price * line.qty, 0)
// 1179

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

Собрать объект из списка:

const tracks = [
  { id: 't1', bpm: 120 },
  { id: 't2', bpm: 90 },
]

const bpmById = tracks.reduce((acc, track) => {
  acc[track.id] = track.bpm
  return acc
}, {})
// { t1: 120, t2: 90 }

find и findIndex #

const line = cart.find((item) => item.sku === 'ms-02')
// объект или undefined

const idx = playlist.findIndex((t) => t.id === 'focus-03')
// индекс или -1

find останавливается на первом совпадении.


some и every #

const hasExpress = deliveryOptions.some((o) => o.type === 'express')
const allPaid = orders.every((o) => o.status === 'paid')

some — «хотя бы один»; every — «все без исключения». Оба коротко-замыкаются: как только результат ясен, перебор прекращается.


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

const result = cart
  .filter((line) => line.qty > 0)
  .map((line) => line.price * line.qty)
  .reduce((sum, amount) => sum + amount, 0)

Читается сверху вниз: отфильтровали → посчитали строки → сложили.


sort: сортировка и ловушка мутации #

sort меняет исходный массив и возвращает ссылку на него:

const nums = [30, 10, 20]
nums.sort((a, b) => a - b)
nums // [10, 20, 30]

Без компаратора элементы приводятся к строкам:

[10, 9, 80].sort() // [10, 80, 9] — лексикографически!

Сортировка по числу:

scores.sort((a, b) => b.points - a.points) // по убыванию очков

Сортировка по строке с локалью:

tracks.sort((a, b) => a.title.localeCompare(b.title, 'ru'))

Безопасная сортировка: копия #

const original = [{ score: 88 }, { score: 120 }, { score: 95 }]

const ranked = [...original].sort((a, b) => b.score - a.score)
// ranked отсортирован, original — в прежнем порядке

const ranked2 = original.slice().sort((a, b) => b.score - a.score)

Паттерн «sort copy» в JSFlow: никогда не сортируйте состояние React/хранилища напрямую, если порядок исходника должен сохраниться.


Другие полезные методы #

const ids = ['a', 'b', 'a', 'c']
ids.includes('b') // true

const flat = [1, [2, 3], [[4]]].flat(2) // [1, 2, 3, 4]

const merged = [1, 2].concat([3, 4]) // [1, 2, 3, 4] — альтернатива spread

playlist.slice(1, 3) // копия участка без мутации

splice — мутирующее вырезание/вставка; в прикладном коде чаще заменяют на filter + spread.


Мутирующие vs немутирующие #

Мутирует массивВозвращает новый
push, pop, shift, unshiftmap, filter, slice, concat
sort, reverse, splice, filltoSorted, toReversed (ES2023, если доступны)

В средах с toSorted:

const order = [3, 1, 2]
const sorted = order.toSorted((a, b) => a - b)
order // [3, 1, 2] — без изменений

Иначе — [...arr].sort(...).


Практика: чекаут #

const cart = [
  { sku: 'kb-01', price: 4900, qty: 1, category: 'gear' },
  { sku: 'ms-02', price: 1290, qty: 0, category: 'gear' },
  { sku: 'sub-01', price: 299, qty: 1, category: 'digital' },
]

function checkoutLines(cart) {
  return cart
    .filter((line) => line.qty > 0)
    .map((line) => ({
      sku: line.sku,
      amount: line.price * line.qty,
    }))
}

function cartTotal(cart) {
  return checkoutLines(cart).reduce((sum, line) => sum + line.amount, 0)
}

checkoutLines(cart)
// [{ sku: 'kb-01', amount: 4900 }, { sku: 'sub-01', amount: 299 }]

cartTotal(cart) // 5199
function topScores(entries, limit = 3) {
  return [...entries]
    .sort((a, b) => b.score - a.score)
    .slice(0, limit)
}

topScores(
  [
    { name: 'Аня', score: 120 },
    { name: 'Борис', score: 200 },
    { name: 'Вика', score: 150 },
  ],
  2
)
// [{ name: 'Борис', ... }, { name: 'Вика', ... }]

Практика: плейлист #

const playlist = [
  { id: 1, title: 'Warmup', duration: 180 },
  { id: 2, title: 'Focus', duration: 240 },
  { id: 3, title: 'Cooldown', duration: 120 },
]

const totalSec = playlist.reduce((sum, t) => sum + t.duration, 0)
const longTracks = playlist.filter((t) => t.duration >= 200)
const titles = playlist.map((t) => t.title)

Итог #

  • map / filter / reduce — три кита преобразования данных; задавайте начальное значение в reduce.
  • find, some, every останавливаются раньше, чем полный перебор, когда ответ уже известен.
  • sort мутирует массив; для UI-состояния сортируйте копию: [...arr].sort(...) или toSorted.
  • Числовая сортировка требует компаратора (a, b) => a - b, иначе сравнение строковое.
  • Цепочки методов читаются лучше вложенных циклов, если каждый шаг делает одну вещь.

См. также #