видалити об'єкти з масиву за властивістю об'єкта


137
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));

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 (deleteIndex, 1);
wojjas

1
@par Парламент - чи є у Вашому прикладі документація на тильду? Я ніколи цього не бачив.
webdad3

43

З подачею / підкресленням:

Якщо ви хочете змінити існуючий масив, тоді нам доведеться використовувати сплайс . Ось трохи кращий / читабельний спосіб за допомогою findWhere підкреслення / подання:

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метод масиву, тому його легко без подання / підкреслення

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

(ES5 підтримується майже у всіх браузерах morden)

Про FindIndex та його сумісність із браузером


як видалити всі елементи з масиву об'єктів, але мені також потрібен зворотній дзвінок
Muhaimin

що ви також маєте на увазі під зворотним дзвінком, ви можете скористатися вищевказаним кодом, щоб видалити об'єкт з масиву ..
Рахул Р.

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);

Мені подобається ця сучасна відповідь, за винятком того, що ваш код не
сприймає

2
дуже проста відповідь порівняно з іншими і працює просто чудово.
Аруна

простий, але потужний asnwer
Шеллі

15

Для видалення об'єкта за допомогою його id в заданому масиві;

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);

7
зауважте, що для цієї відповіді потрібна бібліотека підкреслення
ДжошуаДавид

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;
});


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
Думаю, що це дуже допомогло :)
RAJESH KUMAR ARUMUGAM

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));

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. Цей метод завжди повертає елемент, якщо умова істинна. Отже, якщо ви хочете видалити за допомогою id, ви повинні зберегти весь елемент, який не відповідає заданому ідентифікатору. Ось приклад:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.