在JavaScript中,如何从对象数组中删除一个元素?

4
在JavaScript中,如何从一个对象数组中删除一个元素? 以下是代码:
$.fn.mapImage.deletePinpoint = function(image, pinpoint){
    var deleted = false;
    for (var i = 0; i < image.pinpoints.length; i++) {
        if(image.pinpoints[i].position == pinpoint.position){
            image.pinpoints.remove(i);
            deleted = true;
        }
        if(deleted){
            image.pinpoints[i].position -= 1;
        }
    }
    $('.edit-mode').find('div.dynamic-pinpoint-area').remove();
    $('.edit-mode').find('div.pinpoint-text').remove();
    $('.create-mode').find('div.static-pinpoint-area').remove();
    $('.create-mode').find('div.pinpoint-text').remove();

    $.fn.mapImage.load(image);

}

image.pinpoints是一个对象数组。感谢大家!


有没有可能您能够查看一下这个问题的其他答案,并接受一个合适的答案?被接受的答案链接到一个带有恶意软件的网站,而且并没有真正解释为什么 splice() 是首选的方法。谢谢。 - Kev
3个回答

4

1
请注意,如果您使用delete而不是splice,它不会重新对删除索引后的元素进行编号。 如果您使用'in'语法,这是可以接受的,但在使用for循环时可能会导致麻烦。for(var i=0;i<trees.length;i++) { /* */ } 仍然会被执行5次,而不是您期望的4次。 - nxt
这是正确的。这也是为什么在许多情况下,使用 for (i in arr) 更可取的另一个原因。 - Jonathan Fingland
1
对于遍历数组中的每个元素,使用for (i in arr)非常有用。但是它也会枚举原型链,并且不幸的是许多库会将自己的函数添加到Array原型中。当然,你可以使用hasOwnProperty函数来检查这一点,但我认为只是问如何从数组中删除一个元素的人还没有完全理解使用'in'的后果。 - nxt

1

我认为您应该对问题进行重新表述以使其更加清晰。从您的示例中,如果 position 属性与 pinpoint 相匹配,则可以从image.pinpoints 数组中删除多个元素。因此,它将删除每个 image.pinpoints[i].position == pinpoint.position,其中 i 的取值范围从 0(image.pinpoints.length - 1)

由于您同时正在遍历数组,我不建议仅使用 splice。相反,首先要 delete 每个索引,然后在第二次遍历中清理数组。

splicedelete 的工作方式不同,因为 delete 会在数组中创建一个洞并将已删除的属性的值设置为 undefined。另一方面,splice 将删除元素,就好像它从未存在过,并将其后面的所有元素的索引修复为连续的。请考虑以下示例:

> var a = [2,3,5,7,11]; // create an array of 5 elements
> undefined
> a[2] // see the value of the third element
> 5
> delete a[2] // delete the third element using "delete"
> true
> a // log contents of a
> [2, 3, undefined, 7, 11]
> a[2] // index 2 still exists with value "undefined" now
> undefined

splice 单独使用也存在问题,如果你删除一个元素,那么该元素后面的所有索引都会向上移动一个位置,这样就会跳过检查下一个元素。考虑下面这个第二个例子:

> var a = [2,3,5,7,11]; // create array of 5 elements
> for(var i = 0; i < a.length; i++) { 
    if(a[i] == 3 || a[i] == 5) { // if it's 3 or 5, take it out
        a.splice(i, 1);
    }
}
> a
[2, 5, 7, 11]; // yikes, 5 still exists

在上面的例子中,5仍然存在,因为我们从未检查过该值。当我们看到3时,当前索引是1。在对数组进行拼接后,下一个元素-5向上移动以占据它的位置,并成为索引1。由于我们已经完成了此时的索引1,因此我们将简单地转移到下一个索引-2,它现在具有值7,并跳过5。通常不建议使用索引进行迭代和原地删除。
作为解决方案,我会创建一个新数组,并仅在其中插入不需要删除的属性。
$.fn.mapImage.deletePinpoint = function(image, pinpoint) {
    // will hold all objects that are not to be deleted
    var remainingPinpoints = [];

    for (var i = 0; i < image.pinpoints.length; i++) {
        // reverse condition
        if(image.pinpoints[i].position != pinpoint.position) {
            // add to new array
            remainingPinpoints.push(image.pinpoints[i]);
        }
    }

    // assign new array to pinpoints property
    image.pinpoints = remainingPinpoints;

    ...
}

尽管更多的是评论,但这个答案确实提出了一个非常重要的问题,即OP想要哪种行为。 - Jonathan Fingland
关于您的编辑,您可以进行以下操作:a.splice(i--,1); 并且在splice执行后,i将被递减。 - Jonathan Fingland
@Jonathan - 这样做可以实现,但感觉有些混乱。这会使代码依赖于for循环,因为我们知道下一次迭代将更新i的索引到当前值。如果删除了第0个索引,那么意味着i将是-1。如果稍后添加了代码来跟随此删除操作,则很容易忘记i现在指向无效的数组位置。抱歉只是在增加更多问题,但这个问题并不像乍一看那么简单 :) - Anurag
我认为,如果您想根据索引删除元素,则在进入循环之前99%的情况下会知道索引。在这种情况下,在进入循环之前,可以调用a.splice函数。在基于position属性的示例中,只要立即使用continue关键字开始下一次迭代,使用@Jonathan的建议是完全安全的。 - nxt

1

.splice 是在 w3schools.com http://www.w3schools.com/jsref/jsref_splice.asp 给出的方法。 要从索引为 x 的数组中删除一个元素,您可以使用 trees.splice(x, x+1);。这将删除 x 并返回它(如果需要)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接