удалить объекты из массива по свойству объекта

138
var listToDelete = ['abc', 'efg'];

var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}] // all that should remain

Как удалить объект из массива путем сопоставления свойства объекта?

Только родной JavaScript, пожалуйста.

У меня возникли проблемы с использованием сплайсинга, так как длина уменьшается с каждым удалением. Использование клонирования и сплайсинга по оригинальному индексу все еще оставляет проблему уменьшения длины.

Дэн Канзе
источник
1
Цикл в обратном направлении должен исправить проблему изменения длины
Ян
1
Жаль, что нет стандартного правильного способа удаления элементов из массива объектов. Неудивительно, что существует так много сторонних сценариев. Это основная вещь.
гитара

Ответы:

155

Я полагаю, вы использовали spliceчто-то вроде этого?

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
    }
}

Все, что вам нужно сделать, чтобы исправить ошибку, это уменьшить значение iв следующий раз, а затем (и возврат в обратном направлении также возможен):

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
        i--;
    }
}

Чтобы избежать линейного удаления, вы можете записать элементы массива, которые хотите сохранить над массивом:

var end = 0;

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) === -1) {
        arrayOfObjects[end++] = obj;
    }
}

arrayOfObjects.length = end;

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

const setToDelete = new Set(listToDelete);
let end = 0;

for (let i = 0; i < arrayOfObjects.length; i++) {
    const obj = arrayOfObjects[i];

    if (setToDelete.has(obj.id)) {
        arrayOfObjects[end++] = obj;
    }
}

arrayOfObjects.length = end;

который можно обернуть в хорошую функцию:

const filterInPlace = (array, predicate) => {
    let end = 0;

    for (let i = 0; i < array.length; i++) {
        const obj = array[i];

        if (predicate(obj)) {
            array[end++] = obj;
        }
    }

    array.length = end;
};

const toDelete = new Set(['abc', 'efg']);

const arrayOfObjects = [{id: 'abc', name: 'oh'},
                        {id: 'efg', name: 'em'},
                        {id: 'hij', name: 'ge'}];

filterInPlace(arrayOfObjects, obj => !toDelete.has(obj.id));
console.log(arrayOfObjects);

Если вам не нужно делать это на месте, это Array#filter:

const toDelete = new Set(['abc', 'efg']);
const newArray = arrayOfObjects.filter(obj => !toDelete.has(obj.id));
Ry-
источник
79

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

var removeIndex = array.map(item => item.id)
                       .indexOf("abc");

~removeIndex && array.splice(removeIndex, 1);
парламент
источник
1
Это действительно элегантный ответ.
Скотт Сильви,
Еще одна вещь, которую я хочу, будут отдельные кнопки для каждого объекта в массиве. если я хочу удалить этот конкретный объект в массиве, нажмите кнопку, и это должно быть перемещено в другой вторичный массив. как это сделать . Я использовал угловой JS NG-повтор для создания предметов. Вы можете мне помочь
Тилак Радж
очень красивый и элегантный подход. престижность! Только одна вещь для осторожности; если требуемый индекс не найден в массиве карты, он вернет -1; таким образом, сплайс удалит последний элемент.
MCy
15
Или, если вас смущает тильда в последней строке, вы можете найти это более понятным: (removeIndex> = 0) && array.splice (removeIndex, 1);
wojjas
1
@par Parliament - есть ли в вашем примере документация по тильде? Я никогда не видел этого раньше.
webdad3
43

С lodash / подчеркиванием:

Если вы хотите изменить сам существующий массив, тогда мы должны использовать соединение . Вот немного лучше / читабельный способ с использованием findWhere подчеркивания / lodash:

var items= [{id:'abc',name:'oh'}, // delete me
                  {id:'efg',name:'em'},
                  {id:'hij',name:'ge'}];

items.splice(_.indexOf(items, _.findWhere(items, { id : "abc"})), 1);

С ES5 или выше

( без подчеркивания / подчеркивания )

С ES5 и далее у нас есть findIndexметод для массива, так что его легко без lodash / underscore

items.splice(items.findIndex(function(i){
    return i.id === "abc";
}), 1);

(ES5 поддерживается почти во всех современных браузерах)

О findIndex и его совместимости с браузерами

Рахул Р.
источник
как удалить все элементы в массиве объектов, но мне также нужен обратный вызов
Мухаймин
Что вы подразумеваете под обратным вызовом, вы можете использовать приведенный выше код для удаления объекта из массива ..
Рахул Р.
1
Хороший. Я добавлю это в свой пояс для инструментов
abyrne85
Еще одна вещь, которую я хочу, будут отдельные кнопки для каждого объекта в массиве. если я хочу удалить этот конкретный объект в массиве, нажмите кнопку, и это должно быть перемещено в другой вторичный массив. как это сделать . Я использовал угловой JS NG-повтор для создания предметов. Вы можете мне помочь
Тилак Радж
1
Любите это предложение ES5, поскольку оно может быть даже написано более коротким способомitems.splice(items.findIndex(i => i.id === "abc"), 1)
брамчи
27

findIndex работает для современных браузеров:

var myArr = [{id:'a'},{id:'myid'},{id:'c'}];
var index = arr.findIndex(function(o){
     return o.id === 'myid';
})
if (index !== -1) myArr.splice(index, 1);
fatlinesofcode
источник
Мне нравится этот современный ответ, за исключением того, что ваш код не улавливает случай, когда index = -1
rmcsharry
2
очень простой ответ по сравнению с другими и работает просто отлично.
Арун
простой, но мощный ответ
Шелли,
15

Удалить объект по его идентификатору в данном массиве;

const hero = [{'id' : 1, 'name' : 'hero1'}, {'id': 2, 'name' : 'hero2'}];
//remove hero1
const updatedHero = hero.filter(item => item.id !== 1);
Нареш Ченнури
источник
10

Если вы просто хотите удалить его из существующего массива, а не создавать новый, попробуйте:

var items = [{Id: 1},{Id: 2},{Id: 3}];
items.splice(_.indexOf(items, _.find(items, function (item) { return item.Id === 2; })), 1);
user2704940
источник
7
обратите внимание, что этот ответ требует библиотеки подчеркивания
JoshuaDavid
2
Я бы не рекомендовал это, потому что, если элемент не найден, _.indexOf вернет -1 => items.splice (-1,1)
user1441287
6

Цикл в обратном порядке путем уменьшения, iчтобы избежать проблемы:

for (var i = arrayOfObjects.length - 1; i >= 0; i--) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
    }
}

Или используйте filter:

var newArray = arrayOfObjects.filter(function(obj) {
    return listToDelete.indexOf(obj.id) === -1;
});
Феликс Рабе
источник
5

Проверьте это, используя Set и ES6 filter.

  let result = arrayOfObjects.filter( el => (-1 == listToDelete.indexOf(el.id)) );
  console.log(result);

Вот JsFiddle: https://jsfiddle.net/jsq0a0p1/1/

Мирослав Савовский
источник
1
Работает отлично!
Антони Кепински
Самый современный и элегантный подход на мой взгляд.
Патрис
4

Только родной JavaScript, пожалуйста.

В качестве альтернативы, более «функционального» решения, работающего на ECMAScript 5, вы можете использовать:

var listToDelete = ['abc', 'efg'];
var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}]; // all that should remain

arrayOfObjects.reduceRight(function(acc, obj, idx) {
    if (listToDelete.indexOf(obj.id) > -1)
        arrayOfObjects.splice(idx,1);
}, 0); // initial value set to avoid issues with the first item and
       // when the array is empty.

console.log(arrayOfObjects);
[ { id: 'hij', name: 'ge' } ]

Согласно определению «Array.prototype.reduceRight» в ECMA-262 :

reduRight напрямую не мутирует объект, для которого он вызывается, но объект может быть мутирован вызовами callbackfn .

Так что это правильное использование reduceRight.

Сильвен Леру
источник
2
var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}] // all that should remain

согласно вашему ответу будет так. когда вы щелкаете какой-то конкретный объект, отправляете индекс в параметре для функции delete me. Этот простой код будет работать как шарм.

function deleteme(i){
    if (i > -1) {
      arrayOfObjects.splice(i, 1);
    }
}
Субходжит Мондал
источник
1
Спасибо, это очень помогло :)
РАДЖЕШ КУМАР АРУМУГАМ
1

с фильтром и indexOf

withLodash = _.filter(arrayOfObjects, (obj) => (listToDelete.indexOf(obj.id) === -1));
withoutLodash = arrayOfObjects.filter(obj => listToDelete.indexOf(obj.id) === -1);

с фильтром и включает в себя

withLodash = _.filter(arrayOfObjects, (obj) => (!listToDelete.includes(obj.id)))
withoutLodash = arrayOfObjects.filter(obj => !listToDelete.includes(obj.id));
user3437231
источник
0

Если вам нравятся короткие и информативные параметры, или если вы не хотите использовать spliceи использовать прямой фильтр, или если вы просто человек SQL, как я:

function removeFromArrayOfHash(p_array_of_hash, p_key, p_value_to_remove){
    return p_array_of_hash.filter((l_cur_row) => {return l_cur_row[p_key] != p_value_to_remove});
}

И пример использования:

l_test_arr = 
[
    {
         post_id: 1,
        post_content: "Hey I am the first hash with id 1"
    },
    {
        post_id: 2,
        post_content: "This is item 2"
    },
    {
        post_id: 1,
        post_content: "And I am the second hash with id 1"
    },
    {
        post_id: 3,
        post_content: "This is item 3"
    },
 ];



 l_test_arr = removeFromArrayOfHash(l_test_arr, "post_id", 2); // gives both of the post_id 1 hashes and the post_id 3
 l_test_arr = removeFromArrayOfHash(l_test_arr, "post_id", 1); // gives only post_id 3 (since 1 was removed in previous line)
Мехмет Каплан
источник
-1

Вы можете использовать filter. Этот метод всегда возвращает элемент, если условие истинно. Поэтому, если вы хотите удалить по идентификатору, вы должны сохранить все элементы, которые не соответствуют данному идентификатору. Вот пример:

arrayOfObjects = arrayOfObjects.filter (obj => obj.id! = idToRemove)

Дэвид Бенитес Риба
источник