/ JavaScript

Как я работаю с массивами в JavaScript

В JavaScript есть много доступных методов при работе с массивами. Возможными способами создания или изменения массивов являются: unshift, shift, push, pop, splice, concat, slice, деструктуризация, rest-операторы, spread-операторы.

Существуют также методы для интерации элементов в циклах: for, forEach, map, filter, reduce, find, findIndex.

17 различных вариантов, Карл! 😱.

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

Мутация

Никогда не мутируйте массивы. Это может сломать ваш код, а вы этого и не заметите. И подобные ошибки, к слову, трудно отловить.

Если вам нужно мутировать массив, всегда используйте slice, для создания его копии перед мутацией.

const array = [1, 2, 3]
const copy = array.slice()

// Используйте эти методы уже со slice копией
copy.push(4)
copy.pop()
copy.unshift(0)
copy.shift()
copy.splice(0, 0, 0)

Добавление элементов в массивы

Тут мы рассмотрим, как в JavaScript добавить элемент в массив. Сделать мы это можем тремя способами:

  • В начало массива
  • В конец массива
  • В середину массива

Добавление элементов в начало массива

Когда я добавляю элементы в начало массива, я предпочитаю создавать новый массив с помощью spread-оператора. Это самый чистый способ.
const array = [3, 4, 5]
const after = [1, 2, ...array]

console.log(after) // [1, 2, 3, 4, 5]

Добавление элементов в конец массива

При добавлении элементов в конец массива я также предпочитаю создавать новый массив spread-оператором.
const array = [3, 4, 5]
const after = [...array, 6, 7]

console.log(after) // [3, 4, 5, 6, 7]

Добавление элементов в середину массива

Я предпочитаю splice при добавлении элементов в середину массива. Я делаю это потому, что использование одного только slice кажется более неуклюжим.

Например, у меня есть массив из 25 видов фруктов. Я хочу добавить апельсин после груши Pear. Но я не знаю под каким индексом находится Pear. Сначала я должен найти грушу методом indexOf.

const index = fruits.indexOf('Pear')

Теперь я могу добавить апельсин после груши. Сравните разницу между slice и splice.

// Используя Slice
const result = [
  ...fruits.slice(0, index)
  'Orange',
  ...fruits.slice(index + 1)
]
// Используя Splice
const result = fruits.slice()
result.splice(index + 1, 0, 'Orange')

splice намного легче читать, по сравнению с только slice альтернативой.

Удаление элементов из массива

Здесь рассмотрим подходы в JavaScript для удаления элемента из массива. Мы можем сделать это тремя способами:

  1. С самого начала массива.
  2. Из конца массива.
  3. Из середины массива.

Удаление элементов с самого начала массива

Когда я удаляю элементы из начала массива, я предпочитаю деструктуризировать массив. Это выглядит чище, чем unshift или splice.

const array = [1, 2, 3]
const [throwaway, ...result] = array

console.log(result) // [2, 3]

Удаление элементов с конца массива

Когда я удаляю элементы из конца массива, я предпочитаю использовать slice. Здесь я могу использовать отрицательные индексы вместо array.length. Это делает удаление элемента намного проще.

const array = [1, 2, 3]
const result = array.slice(0, -2)

console.log(result) // [1]

Если мне нужно удалить 1, или 2 элемента, то я всё-таки предпочитаю pop. Это более понятная и дружелюбная запись для новичков.

const array = [1, 2, 3]
const result = array.slice()
result.pop()

console.log(result) // [1, 2]

Удаление элементов с середины массива

В этом случае я предпочитаю использовать splice, по сравнению с другими методами.

// Используя Slice
const result = [
  ...fruits.slice(0, index)
  ...fruits.slice(index + 1)
]
// Используя Splice
const result = fruits.slice()
result.splice(index, 1)

Цикл по массиву

Когда я перебираю элеметны массива в цикле, я предпочитаю использовать map и filter везде, где это возможно. Отлично, если их хватает для моих задач!

// Map
const array = [1, 2, 3]
const doubled = array.map(x => x * 2)

console.log(doubled) // [2, 4, 6]
// Filter
const array = [1, 5, 10]
const below6 = array.filter(x => x < 6)

console.log(below6) // [1, 5]

Я никогда не использую reduce, если я могу воспользоваться map + filter потому что map + filter на порядок легче читать. Я использую reduce только когда мне нужно конвертировать массив в притивное значение (обычно, только при работе с числами).

// Используем reduce по массиву чисел
const array = [1, 2, 3]
const sum = array.reduce((sum, current) => sum + current, 0)

console.log(sum) // 6

Если мне нужно преобразовать массивы в объекты, я предпочитаю использовать цикл forEach.

const fruits = ['apple', 'apple', 'pear']

// С forEach
const tally = {}
fruits.forEach(fruit => {
  if (tally[fruit]) {
    tally[fruit] += 1
    return
  }
  tally[fruit] = 1
})

console.log(tally)
// {
//   apple: 2,
//   pear : 1
// }
// C Reduce
const tally = fruits.reduce((tally, fruit) => {
  if (tally[fruit]) {
    tally[fruit] += 1
  } else {
    tally[fruit] = 1
  }
  return tally
}, {})

console.log(tally)
// {
//   apple: 2,
//   pear : 1
// }

Если мне нужно что-то выполнить (типа изменения классов элементу), я предпочитаю forEach. Я так же могу использовать for...of, но forEach мне нравится больше.

const nodes = document.querySelectorAll('.hey')

// С forEach
[...nodes].forEach(node => {
  node.classList.remove('hey')
})

// С for...of
for (const node of nodes) {
  node.classList.remove('hey')
}

Когда я читаю forEach, мои мысли идут вот так:

  1. Массив Node-узлов.
  2. Пройтись циклом по массиву узлов.
  3. Сделать что-то с каждым из узлов.

Когда я вижу for...of, то что-то вроде:

  1. for...of. Окей.
  2. Создали переменную node.
  3. Проходим циклом по массиву nodes.
  4. Делаем что-то с node.

С for...of почему-то всё не кажется всё так интуитивно понятно, как с forEach.

Если вы работаете с ассоциативными массивами, или объектами, которые нужно обработать в цикле, то ранее мы писали статью на эту тему.

Асинхронные циклы

В этой секции рассмотрим, как работать с списком асинхронных запросов и их обработкой в JavaScript. Если я могу сгруппировать асинхронные вызовы вместе, то я воспользуюсь map, а затем Promise.all.

const array = ['url1', 'url2']
const promises = array.map(url => fetch(url).then(/*...*/))
const results = Promise.all(promises)

console.log(results)
// [
//   [результат с url1], [результат с url2]
// ]

Если я использую оператор await, то более предпочитаю for...of.

async function execute () {
  for (const link of links) {
    await fetch(link).then()
    // что-то делаем...
  }
}

Вот и всё! Надеюсь, эта статья вам помогла! Надеюсь, эта статья здорово помогла вам при работе с массивами в JavaScript.