ลบวัตถุออกจากอาร์เรย์โดยคุณสมบัติของวัตถุ


148
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 เนื่องจากความยาวลดลงเมื่อลบแต่ละครั้ง การใช้โคลนและการเชื่อมต่อกับดัชนี orignal ยังคงทำให้คุณมีปัญหาเรื่องความยาวที่ลดลง


1
การวนกลับไปข้างหลังควรแก้ไขปัญหาการเปลี่ยนแปลงความยาว
เอียน

1
เป็นเรื่องที่น่าเสียดายที่ไม่มีวิธีที่เหมาะสมมาตรฐานในการลบรายการออกจากอาร์เรย์วัตถุ ไม่น่าแปลกใจที่มีสคริปต์ของบุคคลที่สามมากมาย มันเป็นสิ่งพื้นฐาน
กีตาร์

คำตอบ:


159

ฉันคิดว่าคุณใช้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));

86

คุณสามารถลบไอเท็มด้วยคุณสมบัติอย่างใดอย่างหนึ่งโดยไม่ต้องใช้ lib ของบุคคลที่สามเช่นนี้:

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

~removeIndex && array.splice(removeIndex, 1);

1
นี่เป็นคำตอบที่สวยหรูจริงๆ
Scott Silvi

อีกสิ่งหนึ่งที่ฉันต้องการจะมีปุ่มแยกสำหรับทุกวัตถุในอาร์เรย์ หากฉันต้องการลบวัตถุนั้นในปุ่มอาร์เรย์ที่คลิกและควรย้ายไปยังอาร์เรย์รองอื่น ทำอย่างไร . ฉันใช้ angular js ng-repeat เพื่อสร้างรายการ คุณช่วยฉันได้ไหม
Thilak Raj

แนวทางที่ดีและสง่างามมาก ความรุ่งโรจน์! ขอเตือนเพียงเรื่องเดียว หากไม่พบดัชนีที่ต้องการบนอาร์เรย์แผนที่จะส่งกลับ -1; ดังนั้นการประกบจะลบองค์ประกอบสุดท้าย
mcy

16
หรือถ้าคุณสับสนกับเครื่องหมายตัวหนอนในบรรทัดสุดท้ายคุณอาจพบว่าสิ่งนี้ชัดเจนยิ่งขึ้น: (removeIndex> = 0) && array.splice (removeIndex, 1);
wojjas

1
@parliament - มีเอกสารเกี่ยวกับเครื่องหมายตัวหนอนในตัวอย่างของคุณหรือไม่? ฉันไม่เคยเห็นแบบนี้มาก่อน
webdad3

48

ด้วย lodash / underscore:

หากคุณต้องการที่จะปรับเปลี่ยนอาร์เรย์ที่มีอยู่ของตัวเองแล้วเราจะต้องใช้ประกบกัน นี่คือวิธีที่ดีกว่า / อ่านได้เล็กน้อยโดยใช้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 หรือสูงกว่า

( ไม่มี lodash / ขีดล่าง )

ด้วย ES5 เป็นต้นไปเรามีfindIndexวิธีการในอาร์เรย์ดังนั้นจึงง่ายโดยไม่ต้องมี lodash / underscore

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

(ES5 ได้รับการสนับสนุนในเบราว์เซอร์ morden เกือบทั้งหมด)

เกี่ยวกับ findIndexและความเข้ากันได้ของเบราว์เซอร์


ฉันจะลบรายการทั้งหมดในอาร์เรย์ของออบเจ็กต์ได้อย่างไร แต่ฉันก็ต้องการการติดต่อกลับเช่นกัน
Muhaimin

คุณหมายถึงอะไรโดยการโทรกลับเช่นกันคุณสามารถใช้รหัสด้านบนเพื่อลบวัตถุออกจากอาร์เรย์ได้ ..
Rahul R.

1
ทำได้ดีนี่. ฉันจะเพิ่มสิ่งนี้ใน
แถบ

อีกสิ่งหนึ่งที่ฉันต้องการจะมีปุ่มแยกสำหรับทุกวัตถุในอาร์เรย์ หากฉันต้องการลบวัตถุนั้นในปุ่มอาร์เรย์ที่คลิกและควรย้ายไปยังอาร์เรย์รองอื่น ทำอย่างไร . ฉันใช้ angular js ng-repeat เพื่อสร้างรายการ คุณช่วยฉันได้ไหม
Thilak Raj

4
ชอบข้อเสนอแนะ ES5 นี้เนื่องจากสามารถเขียนได้ในวิธีที่สั้นกว่าitems.splice(items.findIndex(i => i.id === "abc"), 1)
bramchi

29

findIndex ใช้ได้กับเบราว์เซอร์สมัยใหม่:

var myArr = [{id:'a'},{id:'myid'},{id:'c'}];
var index = myArr.findIndex(function(o){
  return o.id === 'myid';
})
if (index !== -1) myArr.splice(index, 1);

ฉันชอบคำตอบที่ทันสมัยนี้ยกเว้นรหัสของคุณจับไม่ได้เมื่อ index = -1
rmcsharry

2
คำตอบที่ง่ายมากเมื่อเทียบกับคนอื่น ๆ และใช้งานได้ดี
อรุณ

เรียบง่าย แต่ทรงพลัง
Shelly

16

ในการลบวัตถุโดยใช้ 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
สังเกตว่าคำตอบนี้ต้องใช้ไลบรารีขีดล่าง
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

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

นี่คือ JsFiddle: https://jsfiddle.net/jsq0a0p1/1/


1
ทำงานได้อย่างสมบูรณ์แบบ!
Antoni Kepinski

แนวทางที่ทันสมัยและสง่างามที่สุดในมุมมองของฉัน
AC Patrice

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 :

reduceRight ไม่ได้โดยตรงกลายพันธุ์วัตถุที่มันถูกเรียกว่าแต่วัตถุที่อาจจะกลายพันธุ์โดยการโทรไปยัง 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

ตามคำตอบของคุณจะเป็นเช่นนี้ เมื่อคุณคลิกวัตถุบางอย่างให้ส่งดัชนีในพารามิเตอร์สำหรับฟังก์ชันลบฉัน รหัสง่ายๆนี้จะทำงานเหมือนมีเสน่ห์

function deleteme(i){
    if (i > -1) {
      arrayOfObjects.splice(i, 1);
    }
}

1
Thnaks สิ่งนี้ช่วยได้มาก :)
RAJESH KUMAR ARUMUGAM

1

ด้วยตัวกรองและดัชนีของ

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)

0

คุณสามารถใช้filter. วิธีนี้จะส่งคืนองค์ประกอบเสมอหากเงื่อนไขเป็นจริง ดังนั้นหากคุณต้องการลบทีละ id คุณต้องเก็บองค์ประกอบทั้งหมดที่ไม่ตรงกับ id ที่กำหนด นี่คือตัวอย่าง:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.