Почему элементы массива не изменяются внутри метода «фильтр»?

#javascript #arrays

Вопрос:

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

 function zeroFill() {
  let list = [1, 0, 1, 2, 3, 0, 0, 4, 0]
  
  console.log(list)
  
  const multiplyByTwo = (ar) => ar.filter((word, index, arr) => {
    arr[index   1] *= 2
    
    return true
  })
  
  console.log(multiplyByTwo(list))
} 

Все, кроме первого элемента массива, будут изменены. Если я изменю значение arr[index 1] на arr[index] , ни один из элементов не будет изменен. Я пытаюсь понять, как это работает.

Ответ №1:

Как вы уже знаете map , это правильный способ достижения упомянутого требования, позвольте мне попытаться объяснить, почему filter это не работает.

Вы должны понимать возвращаемое значение фильтра, которое представляет собой новый массив с элементами, прошедшими тест.

 function zeroFill() {
  let list = [1, 0, 1, 2, 3, 0, 0, 4, 0];
  console.log("before = ", list);
  const multiplyByTwo = (ar) =>
    ar.filter((word, index, arr) => {
      arr[index] *= 2;   // <--- Manipulating the current list
      return true;     // <----- Return the current element: word
    });
  console.log("after = ", multiplyByTwo(list));
}

zeroFill();
 

В случае arr[index] * 2 , вы умножаете каждый элемент на 2 (и list тоже изменяете массив), но возвращаете текущий элемент, представленный word в приведенном выше коде.

Ответ №2:

filter возвращает новый массив с элементами массива list на основе его возвращаемого значения. Значения массива изменяются в обоих случаях, т. е. при использовании arr[index 1] *= 2 и arr[index] *= 2 . Однако значение, которое попадает в результирующий массив, совпадает с переданным значением word .

Смотрите спецификацию:

  1. Элемент массива с индексом k массива O извлекается и сохраняется как значение.
  2. Функция обратного вызова вызывается с аргументами (значение, k, O).
  3. Если функция обратного вызова возвращает истинное значение, поместите значение kValue в результирующий массив.

Ни в коем случае элемент массива с индексом k массива O не извлекается снова.

Ваша модификация с arr[index] *= 2 всегда приходит слишком поздно; значение, которое будет добавлено, уже было извлечено из массива filter методом. Ваши изменения с arr[index 1] *= 2 помощью изменят следующий индекс, который является значением, filter получаемым на следующей итерации. Вот почему вы видите измененные значения, начиная с индекса 1 . Индекс 0 еще не видел изменений, поэтому filter получает исходное значение и добавляет его в результирующий массив.

Регистрируйтесь list не только multiplyByTwo(list) для того , чтобы видеть измененные значения.

Вы злоупотребляете filter здесь. Вместо этого вы должны использовать map для изменения каждого значения на другое значение или forEach , если вы хотите изменить массив с помощью чего-то подобного arr[index 1] *= 2 .

Чтобы умножить каждый элемент на два:

 console.log(list.map((value) => value * 2));
 

Комментарии:

1. это очень помогает. В основном я пытался понять поведение метода фильтрации.

Ответ №3:

filter Функция не предназначена для этой цели. Он предназначен для фильтрации необходимых вам элементов массива.

Для вашего конкретного случая я бы использовал .map метод, так как он будет изменять массив по мере необходимости и предназначен для обхода массивов.

 const multiplyByTwo = (ar) => ar.map((current) => current * 2);
 

Ответ №4:

Вы пытаетесь изменить массив, .filter что не является хорошей практикой. Вы можете использовать .map , когда хотите что-то сделать с элементами массива.

 function zeroFill() {
  let list = [1, 0, 1, 2, 3, 0, 0, 4, 0]
  console.log(list)
  const multiplyByTwo = ( ar) => ar.map( (item, index, ) => {
    return item *= 2
    
  })
  console.log(multiplyByTwo(list))
}
zeroFill(); 

Ответ №5:

Сначала я попытаюсь объяснить, почему у вас такое поведение, запустите этот код и попытайтесь понять:

 let list = [1, 0, 1, 2, 3, 0, 0, 4, 0]

function zeroFill() {
  console.log('list before filter: ', list)
  const multiplyByTwo = ( ar) => ar.filter((word, index, arr) => {
    console.log(arr === list); //arr and list have the same reference
    arr[index] *= 2; //you will be modifing list[index], that is the current value being filtered, so you you will get the old value
    //arr[index   1] *= 2; //you will modifying list[index   1], so in the next loop the value will be the new one
    return true
  }); //The result of filter will create a new array
  
  console.log('multiplyByTwo result:', multiplyByTwo(list))
  console.log('list after filter: ', list);
}
 

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

 const list = [1, 0, 1, 2, 3, 0, 0, 4, 0];
const multiplyByTwo  = list.map((current) => current * 2);
 

Ответ №6:

Array.filter() , как указано в названии, фильтрует массив и возвращает новый массив, содержащий элементы, прошедшие фильтр.

Что вам нужно, так это Array.map() если вам нужен сопоставленный (а также новый) массив. В противном случае вы можете использовать Array.forEach()

 // In this function, a new array is created and returned, with modified items
function zeroFillWithMap() {
  let list = [1, 0, 1, 2, 3, 0, 0, 4, 0]
  const multiplyByTwo = list.map(i => i*2)
  console.log('Mapped New Array', multiplyByTwo)
}

zeroFillWithMap();


// In this function, a new array is NOT created and the items of the original array are modified
function zeroFillWithForEach() {
  let list = [1, 0, 1, 2, 3, 0, 0, 4, 0]
  
  list.forEach((item, index, originalArray) => originalArray[index] *= 2)
  console.log('Modified Original Array', list)
}

zeroFillWithForEach(); 

Ответ №7:

Я не уверен, знаете ли вы о «Методе отображения в JS». Я думаю, что проблема, которую вы предложили, может быть решена гораздо проще с помощью этого метода. Я оставлю здесь свой код о том, как я решил вашу проблему.

 const zeroFill = (list) => {
  console.log("orignal array: "   list);
  const multiplyByTwo = list.map((item) => {
    return item * 2;
  });
  console.log("multiplied by two array: "   multiplyByTwo);
};

zeroFill([1, 0, 1, 2, 3, 0, 0, 4, 0]); 

но если вам нужно, чтобы ваша проблема была решена только методом фильтрации, я был бы рад помочь и в этой ситуации.

И да, если вы хотите узнать больше о «Методе отображения в Javascript». Я собираюсь связать несколько статей из MDN и W3SCHOOL.

https://www.w3schools.com/jsref/jsref_map.asp

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map