Клавиша / esc

.with()

Создаёт новый массив, изменяя один элемент исходного.

Время чтения: 5 мин

Кратко

Скопировано

Метод with() изменяет значение одного из элементов массива и возвращает новый массив, без изменения исходного массива.

Пример

Скопировано

Изменим элемент 'white' на 'blue':

        
          
          const colors = ['red', 'green', 'white']const newColors = colors.with(2, 'blue')console.log(newColors)// ['red', 'green', 'blue']// Исходный массив остался прежнимconsole.log(colors)// ['red', 'green', 'white']
          const colors = ['red', 'green', 'white']
const newColors = colors.with(2, 'blue')
console.log(newColors)
// ['red', 'green', 'blue']

// Исходный массив остался прежним
console.log(colors)
// ['red', 'green', 'white']

        
        
          
        
      

Изменим элемент 'white' на 'blue', используя отрицательный индекс:

        
          
          const colors = ['red', 'green', 'white']const newColors = colors.with(-1, 'blue')console.log(newColors)// ['red', 'green', 'blue']// Исходный массив остался прежнимconsole.log(colors)// ['red', 'green', 'white']
          const colors = ['red', 'green', 'white']
const newColors = colors.with(-1, 'blue')
console.log(newColors)
// ['red', 'green', 'blue']

// Исходный массив остался прежним
console.log(colors)
// ['red', 'green', 'white']

        
        
          
        
      

Как пишется

Скопировано

Array.with() принимает два аргумента:

  • индекс элемента, который нужно изменить;
  • новое значение изменяемого элемента.

Индекс элемента может быть:

  • положительный — для доступа к элементам от начала массива;
  • отрицательный — для доступа к элементам с конца массива. Например, -1 — индекс последнего элемента, -2 — предпоследнего и т. д.

Array.with() возвращает массив с изменённым элементом.

Вызов with() без аргументов, не приведёт к ошибке. Это равнозначно вызову with(0, undefined):

        
          
          const routes = ['/home', '/settings', '/about']const newRoutes = routes.with()console.log(newRoutes)// [undefined, '/settings', '/about']
          const routes = ['/home', '/settings', '/about']
const newRoutes = routes.with()
console.log(newRoutes)
// [undefined, '/settings', '/about']

        
        
          
        
      

Попытка вызвать with() со значением индекса за пределами допустимых значений (index >= array.length || index < -array.length) приведёт к возникновению ошибки RangeError.

        
          
          const authors = ['Ф. Кафка', 'Д. Сэлинджер']try {  console.log(authors.with(2, 'К. Воннегут'))} catch (err) {  console.error('Поймали ошибку! Вот она: ', err.message)}// Поймали ошибку! Вот она: Invalid index : 2
          const authors = ['Ф. Кафка', 'Д. Сэлинджер']

try {
  console.log(authors.with(2, 'К. Воннегут'))
} catch (err) {
  console.error('Поймали ошибку! Вот она: ', err.message)
}
// Поймали ошибку! Вот она: Invalid index : 2

        
        
          
        
      

Как понять

Скопировано

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

        
          
          const updateFirstItem = (array, value) => {  return array.with(0, value)}
          const updateFirstItem = (array, value) => {
  return array.with(0, value)
}

        
        
          
        
      

Проверим, как она работает, и убедимся, что исходный массив остался прежним.

        
          
          const bears = ['гризли', 'полярный', 'бурый']const result = updateFirstItem(bears, 'панда')console.log(result)// ['панда', 'полярный', 'бурый']console.log(bears)// ['гризли', 'полярный', 'бурый']
          const bears = ['гризли', 'полярный', 'бурый']
const result = updateFirstItem(bears, 'панда')
console.log(result)
// ['панда', 'полярный', 'бурый']

console.log(bears)
// ['гризли', 'полярный', 'бурый']

        
        
          
        
      

А вот для сравнения функция, меняющая значение первого элемента в исходном массиве:

        
          
          const updateFirstItemDanger = (array, value) => {  const result = array  result[0] = value  return result}
          const updateFirstItemDanger = (array, value) => {
  const result = array
  result[0] = value
  return result
}

        
        
          
        
      

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

        
          
          const bears = ['гризли', 'полярный', 'бурый']const result = updateFirstItemDanger(bears, 'панда')console.log(result)// ['панда', 'полярный', 'бурый']console.log(bears)// Ой, куда пропал мишка гризли!// ['панда', 'полярный', 'бурый']
          const bears = ['гризли', 'полярный', 'бурый']
const result = updateFirstItemDanger(bears, 'панда')
console.log(result)
// ['панда', 'полярный', 'бурый']

console.log(bears)
// Ой, куда пропал мишка гризли!
// ['панда', 'полярный', 'бурый']

        
        
          
        
      

Обратите внимание: причина изменения исходного массива при использовании функции updateFirstItemDanger() в том, что функция не копирует полученный в качестве аргумента исходный массив в новый, а создаёт ещё одну ссылку на исходный массив. Подробнее об этом можно почитать в разделе «Хранение по ссылке и по значению».

Подсказки

Скопировано

💡 with() — это удобный способ избежать изменения (мутации) исходного массива.

💡 with() упрощает изменение элемента, если элементы нужно отсчитывать от конца массива, а не от его начала. Например, это удобно для изменения последнего элемента. Обычно для изменения последнего элемента массива нужно определить его индекс от начала массива. Для это потребуется:

  • узнать длину массива;
  • вычислить индекс последнего элемента, используя формулу lastIndex = arrayLength - 1.

Меняем последний элемент, используя традиционный подход:

        
          
          const words = ['первый', 'второй', 'третий']const lastIndex = words.length - 1// Создаём копию массиваconst newWords = [...words]newWords[lastIndex] = 'последний'console.log(newWords)// ['первый', 'второй', 'последний']
          const words = ['первый', 'второй', 'третий']
const lastIndex = words.length - 1

// Создаём копию массива
const newWords = [...words]

newWords[lastIndex] = 'последний'

console.log(newWords)
// ['первый', 'второй', 'последний']

        
        
          
        
      

А вот как это можно сделать с использованием with():

        
          
          const words = ['первый', 'второй', 'третий']console.log(words.with(-1, 'последний'))// ['первый', 'второй', 'последний']
          const words = ['первый', 'второй', 'третий']
console.log(words.with(-1, 'последний'))
// ['первый', 'второй', 'последний']

        
        
          
        
      

💡 with() может быть использован при объединении функций обработки массива в цепочку вызовов. Каждый метод в цепочке получает результат работы предыдущего. Например:

        
          
          const days = ['', 'пн', 'вт', 'ср', 4]console.log(  days    .filter(item => typeof item === 'string')    .with(0, 'вс')    .map(item => item.toUpperCase()))// [ 'ВС', 'ПН', 'ВТ', 'СР' ]
          const days = ['', 'пн', 'вт', 'ср', 4]

console.log(
  days
    .filter(item => typeof item === 'string')
    .with(0, 'вс')
    .map(item => item.toUpperCase())
)
// [ 'ВС', 'ПН', 'ВТ', 'СР' ]

        
        
          
        
      

💡 При создании нового массива with() выполнит преобразование всех незаполненных ячеек к undefined:

        
          
          const numbers = [0, , 11, 20, , 30]console.log(numbers.with(2, 10))// [ 0, undefined, 10, 20, undefined, 30 ]
          const numbers = [0, , 11, 20, , 30]

console.log(numbers.with(2, 10))
// [ 0, undefined, 10, 20, undefined, 30 ]

        
        
          
        
      

💡 Поддержка метода with() в основных браузерах и в Node.js появилась сравнительно недавно. Например, попытка использовать with() в Node.js v.18.19.0 приведёт к ошибке:

        
          
          const array = ['ночь','улица','фонарь']try {  console.log(array.with(-1,'январь'))} catch (err) {  console.error('Поймали ошибку! Вот она: ', err.message)}// Поймали ошибку!// Вот она: array.with is not a function
          const array = ['ночь','улица','фонарь']
try {
  console.log(array.with(-1,'январь'))
} catch (err) {
  console.error('Поймали ошибку! Вот она: ', err.message)
}
// Поймали ошибку!
// Вот она: array.with is not a function