1. ホーム
  2. javascript

[解決済み] オブジェクトのプロパティで配列からオブジェクトを削除する

2022-04-21 09:22:49

質問

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のみでお願いします。

削除するたびに長さが減っていくので、spliceを使うのは難しいです。 クローンやオリジナルインデックスへのスプライスを使っても、長さが減少する問題が残ります。

解決方法は?

を使用していると思います。 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));