Изменение массива на месте при использовании .filter и .forEach

#javascript #arrays

#javascript #массивы

Вопрос:

Я хотел бы отфильтровать массив объектов и обновить некоторое значение для отфильтрованных записей. Вот мой исходный код, без фильтрации:

 let obj = {
  foo: [
    {
      bar: "I should be modified",
      modify: true,
    },{
      bar: "I should not be modified",
      modify: false,
    },{
      bar: "I should be modified",
      modify: true,
    },
  ],
};

obj.foo.forEach((item, i) => {
  let newItem = Object.assign({}, item);
  newItem.bar = "I have been modified";
  obj.foo[i] = newItem;
});

console.log(obj.foo);
/* Output:
  [
    {
      bar: "I have been modified",
      modify: true,
    },{
      bar: "I have been modified",
      modify: false,
    },{
      bar: "I have been modified",
      modify: true,
    },
  ],
*/
  

Теперь я хотел бы заменить .forEach(... на .filter(e => e.modify).forEach(... . Таким образом, вместо изменения всех элементов obj.foo , изменяются только те, у modify === true которых. Как это можно сделать без повторной фильтрации внутри цикла forEach?

 obj.foo.filter(e => e.modify).forEach((item, i) => {
  let newItem = Object.assign({}, item);
  newItem.bar = "I have been modified";
  obj.foo[i] = newItem; // will not work as the index i is not correct from the original array
});

console.log(obj.foo);

/* Expected output:
  [
    {
      bar: "I have been modified",
      modify: true,
    },{
      bar: "I should not be modified",
      modify: false,
    },{
      bar: "I have been modified",
      modify: true,
    },
  ],
*/

/* Actual output:
  [
    {
      bar: "I have been modified",
      modify: true,
    },{
      bar: "I have been modified",
      modify: true,
    },{
      bar: "I should be modified",
      modify: true,
    },
  ],
*/

  

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

1. Какой ожидаемый результат здесь? Если вы измените фильтр, как вы измените свойство bar?

2. Я думаю, вам не придется снова фильтровать в цикле forEach независимо. Что не так с добавлением .filter(e => e.modify).forEach(... ?

3. obj.foo.filter((item, i) => { if(item.modify === true) { все, что вы хотите изменить, идет сюда; вернуть элемент; } });

4. Если вы используете filter метод, вы потеряете правильный индекс элемента. Таким образом, без фильтрации вы должны проверять с помощью if инструкции внутри forEach обратного вызова.

5. вы должны сначала сопоставить его, а затем отфильтровать с помощью сопоставленного индекса … также, пожалуйста, предоставьте свой вывод

Ответ №1:

 let obj = {
  foo: [
    {
      bar: "baz",
      modify: true,
    },{
      bar: "bad",
      modify: false,
    },{
      bar: "hello",
      modify: true,
    },
  ],
};

const {foo} = obj;

const t = foo.reduce((acc, curr) => {
  if (curr.modify) {
    curr.bar = "I have been modified";
    acc.push(curr);
  }
  return acc;
}, []);

console.log(t)  

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

1. Я думаю, что у вас есть ошибка, acc.push(curr) которая должна быть вне инструкции if.

2. Да, если вам не нужно фильтровать массив с помощью параметра «изменить»

Ответ №2:

Это также можно сделать следующим образом:

 obj.foo.filter(e => e.modify).map((item) => {
  item.bar = "I have been modified"
});
  

Я думаю, это должно помочь.

Ответ №3:

Итак, после прочтения всех комментариев, что вы не хотите, чтобы в нем был оператор if и другие вещи, тогда мне приходит в голову эта идея, не изменяя слишком много вашего кода, почему бы не позволить каждому элементу использовать свой собственный исходный индекс, после изменения удалите свойство idx:

 let obj = {
  foo: [
    {
      bar: "I should be modified",
      modify: true,
    },{
      bar: "I should not be modified",
      modify: false,
    },{
      bar: "I should be modified",
      modify: true,
    },
  ],
};
let index = 0;

obj.foo.filter(e =>    index amp;amp; e.modify amp;amp; (e.idx = index)).forEach((item, i) => {
  let newItem = Object.assign({}, item);
  newItem.bar = "I have been modified";
  obj.foo[newItem.idx - 1] = newItem; 
  delete newItem.idx;
});

console.log(obj.foo);