views:

93

answers:

4

In javascript, how do I remove an element from an array of objects? Here is the code:

$.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 is the array of objects. Thanks again guys!

+3  A: 

See https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/delete_Operator

e.g. (from source)

var trees = ["redwood","bay","cedar","oak","maple"];  
delete trees[3];  
if (3 in trees) {  
   // this does not get executed  
}  
Jonathan Fingland
Note that if you use delete instead of splice it doesn't renumber the elements that come after the index you delete.This is fine if you use the 'in' syntax but it could cause trouble when using a for loop.for(var i=0;i<trees.length;i++) { /* */ }is still executed 5 times instead of the 4 you'd expect.
nxt
this is true. which is another reason why for (i in arr) is preferable in many cases.
Jonathan Fingland
for (i in arr) can be very useful, but it also enumerates the prototype chain and unfortunately there are many libraries who add their own functions to the Array prototype. Of course you can check for this using the hasOwnProperty function but I think that someone who just asks how to delete an item from an array is not yet able to fully grasp the consequences of using 'in'
nxt
a fair point, nxt
Jonathan Fingland
+3  A: 

You can also use the splice() method: http://codepunk.hardwar.org.uk/ajs44.htm.

frabjousB
+1  A: 

I think you should rephrase the question to be more clear. From your example, it looks like multiple elements can get deleted from the image.pinpoints array if the position property matches that of pinpoint. So it will delete each image.pinpoints[i].position == pinpoint.position where i goes from 0 to (image.pinpoints.length - 1).

Since you are also iterating through the array at the same time, I wouldn't recommend using splice by itself. Instead delete each index first, and then cleanup the array in a second pass.

splice and delete will work differently as delete creates a hole in the array and sets the deleted property's value to undefined. On the other hand, splice will remove the element as if it never existed, and fix the indexes of all elements after it to be contiguous. Consider this example:

> 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 here by itself is also problematic as if you delete an element, all indexes after that element will shift one up, and you will skip checking the next element. Consider this second example:

> 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

In the above example, 5 is still present as we never checked that value. When we saw the 3, the current index was 1. After splicing the array, the next element - 5 moved up to take it's spot and became index 1. Since we're already done with index 1 at this point, we will simply move onto the next index - 2, which now has value 7, and skip 5. In general it's not a good practice to iterate using indexes, and do in-place removals.

As a solution, I would create a new array and only insert the properties which are not to be deleted in it.

$.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;

    ...
}
Anurag
although more of a comment, this answer does raise a very significant question about which behaviour the OP wants
Jonathan Fingland
with regards to your edit, you could do: a.splice(i--,1); and i will be decremented after the splice is executed
Jonathan Fingland
@Jonathan - that will work, however, it feels messy :) It makes the code dependent on the for loop as we know that the next iteration will update the index of `i` to the current value. If the 0th index was deleted, then that means `i` will be `-1`. And if code was later to be added following this deletion, it's easy to forget that `i` points to an invalid array location now. Sorry to be only throwing more problems at this, but this question is not as trivial as it seems at first sight :)
Anurag
@Anurag I think that if you want to delete elements based upon their index you'll know the index in 99% of the cases before you enter the loop. In that case you could call the a.splice function before entering the loop.In the example where the removal is based on the position property it is perfectly safe to use @Jonathan suggestion as long as you immediately use the continue keyword to start the next iteration.
nxt
+1  A: 

.splice is the method given at w3schools.com http://www.w3schools.com/jsref/jsref_splice.asp To remove one element from an array with index x you would have trees.splice(x,x+1); This removes x and returns it if you need it.

qw3n