Javascript ES6 / ES5 найти в массиве и изменить


134

У меня есть массив объектов. Хочу найти по какому-то полю, а потом его изменить:

var item = {...}
var items = [{id:2}, {id:2}, {id:2}];

var foundItem = items.find(x => x.id == item.id);
foundItem = item;

Я хочу, чтобы он изменил исходный объект. Как? (Мне все равно, будет ли это тоже в lodash)


Ваш новый объект itemсодержит idключ? или вы не против иметь идентификатор, а также все свойства itemобъекта в записи массива?
— Кушик Чаттерджи,

Ответы:


253

Вы можете использовать findIndex чтобы найти индекс в массиве объекта и при необходимости заменить его:

var item = {...}
var items = [{id:2}, {id:2}, {id:2}];

var foundIndex = items.findIndex(x => x.id == item.id);
items[foundIndex] = item;

Это предполагает уникальные идентификаторы. Если ваши идентификаторы дублируются (как в вашем примере), вероятно, лучше использовать forEach:

items.forEach((element, index) => {
    if(element.id === item.id) {
        items[index] = item;
    }
});

16
@georg Это вернет новый массив.
— CodingIntrigue

3
Функция => не будет работать в IE11. Недавно укусил этим.
— Lewis Cianci

1
может быть лучше было бы использовать letключевое слово вместоvar
— Inus Saha

К вашему сведению, это не работает в некоторых версиях phantomJS
— Сид

1
Я предпочитаю более подробный метод, который использует @CodingIntrigue, а не однострочный, mapкоторый использует @georg. Чтобы понять, что происходит, требуется меньше умственной гимнастики. Стоит лишняя строка кода.
— Джошуа Пинтер

44

Мой лучший подход:

var item = {...}
var items = [{id:2}, {id:2}, {id:2}];

items[items.findIndex(el => el.id === item.id)] = item;

Ссылка для findIndex

И если вы не хотите заменять новый объект, а вместо этого копировать поля item, вы можете использовать Object.assign:

Object.assign(items[items.findIndex(el => el.id === item.id)], item)

в качестве альтернативы .map():

Object.assign(items, items.map(el => el.id === item.id? item : el))

Функциональный подход :

Не изменяйте массив, используйте новый, чтобы не создавать побочных эффектов.

const updatedItems = items.map(el => el.id === item.id ? item : el)

1
Пожалуйста, дайте ссылку на английскую страницу, если исходный вопрос был на английском языке. Кроме того, в этом примере предполагается, что объект всегда находится.
— raarts

1
Тогда вы всегда можете заключить это в выражение try catch ... не так ли?
— Сольдеплата Сакетос

1
И, будучи полностью буквальным к вопросу сообщений, он хочет отредактировать элемент в массиве. Он не хочет знать, существует он или нет, поэтому мы предполагаем, что он уже делал это раньше.
— Сольдеплата Сакетос

@SoldeplataSaketos: да, вы можете обернуть его в try/catch, но не должны, потому что отсутствие элемента не является исключительным случаем; это стандартный случай, который вы должны учитывать, проверяя возвращаемое значение, findIndexа затем обновляя массив только тогда, когда элемент был найден.
— Уэйн

20

Другой подход - использовать сращивание .

splice()Метод изменяет содержимое массива путем удаления или замены существующих элементов и / или добавление новых элементов на месте .

NB: если вы работаете с реактивными фреймворками, он обновит «представление», ваш массив будет «знать», что вы его обновили.

Ответ :

var item = {...}
var items = [{id:2}, {id:2}, {id:2}];

let foundIndex = items.findIndex(element => element.id === item.id)
items.splice(foundIndex, 1, item)

А если вы хотите изменить только значение элемента, вы можете использовать функцию поиска :

// Retrieve item and assign ref to updatedItem
let updatedItem = items.find((element) => { return element.id === item.id })

// Modify object property
updatedItem.aProp = ds.aProp

15

Учитывая измененный объект и массив:

const item = {...}
let items = [{id:2}, {id:3}, {id:4}];

Обновите массив новым объектом, перебирая массив:

items = items.map(x => (x.id === item.id) ? item : x)

Пожалуйста, не вставляйте просто код. Объясните, что делается и как это решает проблему.
— Спенсер

1
Я думаю, что это лучшее решение, так как у него лучшая производительность, поскольку он проходит по массиву только один раз, а также меняет ссылку на массив, поэтому он избегает изменчивых ситуаций
— Охад Садан

6

Можно использовать фильтр .

const list = [{id:0}, {id:1}, {id:2}];
let listCopy = [...list];
let filteredDataSource = listCopy.filter((item) => {
       if (item.id === 1) {
           item.id = 12345;
        }

        return item;
    });
console.log(filteredDataSource);

Массив [объект {id: 0}, объект {id: 12345}, объект {id: 2}]


Мне нравится фильтр, потому что он позволяет создать новый массив, и для этого также «удаляются» несуществующие записи
— pungggi

0

работал на меня

let returnPayments = [ ...this.payments ];

returnPayments[this.payments.findIndex(x => x.id == this.payment.id)] = this.payment;

1
Пожалуйста, не вставляйте просто код. Объясните, что делается и как это решает проблему.
— Адриан Моул

Принятый ответ, получивший наибольшее количество голосов, не сильно отличается, но вы не указали на то, что они обновят свой ответ ... почему это так?
— li x

0

Хотя большинство существующих ответов прекрасны, я хотел бы включить ответ с использованием традиционного цикла for, который также следует здесь рассмотреть. OP запрашивает ответ, совместимый с ES5 / ES6, и применяется традиционный цикл for :)

Проблема с использованием функций массива в этом сценарии заключается в том, что они не изменяют объекты, но в этом случае изменение является обязательным. Прирост производительности от использования традиционного цикла for - это просто (огромный) бонус.

const findThis = 2;
const items = [{id:1, ...}, {id:2, ...}, {id:3, ...}];

for (let i = 0, l = items.length; i < l; ++i) {
  if (items[i].id === findThis) {
    items[i].iAmChanged = true;
    break;
  }
}

Хотя я большой поклонник функций массива, не позволяйте им быть единственным инструментом в вашем наборе инструментов. Если целью является изменение массива, они не подходят.


0

Однострочный с использованием оператора распространения.

 const updatedData = originalData.map(x => (x.id === id ? { ...x, updatedField: 1 } : x));
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.