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.