-1

I'm trying to delete all matched items from an array but it leaves Always one item in it.

var item1 = {item: "item1"},
    array = [{
        item: "item1"},{
        item: "item_non"},{
        item: "item1"},{
        item: "item_non"},{
        item: "item1"},{
        item: "item1"},{
        item: "item1"},{
        item: "item_non"},{
        item: "item_non"
    }];
array.forEach(function(items){
    if(item1.item === items.item){
        var index = array.indexOf(items);
        if(index !== -1){
            array.splice(index,1);
        }
    }
});

I also fiddle it, it deletes only 4/5 items that matches instead of 5/5.

Fiddle

There is no option to use Array#filter I need to delete the objects.

I NA
  • 137
  • 1
  • 6
  • 5
    Modifying the array while iterating via `.forEach()` is the fundamental problem. The right way to do this is with `.filter()` or with a simple `for` loop. If you *think* you can't use `.filter()`, you should explain why. – Pointy Oct 21 '15 at 23:04
  • When you remove an item, you change following indexes. You can fix it by iterating backwards. – Oriol Oct 21 '15 at 23:05
  • Related: http://stackoverflow.com/questions/25994909/difference-between-foreach-and-for-loop-in-javascript – Roko C. Buljan Oct 21 '15 at 23:19

3 Answers3

6

The problem is that .splice() moves all the elements after the deleted element down. So if you delete element 3, element 4 becomes 3, 5, becomes 4, and so on. The next iteration of the loop will process element 4, but that's the original element 5 -- the original element 4 is skipped.

The way to solve this is to process the array in reverse. .forEach can't do this, AFAIK, so you have to use a for loop:

for (var i = array.length - 1; i >= 0; i--) {
    item = array[i];
    if (item1.item == item.item) {
        array.splice(i, 1);
    }
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
0

Here's another version that does in-place manipulation of the array.

http://codepen.io/anon/pen/XmZrww?editors=001

// remove the matching item and return the array
var filterArray = function (arr, obj) {
  for (var i = 0, len = arr.length; i < len; i++) {
    if (arr[i].item === obj.item) {
      arr.splice(i, 1);
      len--;
      i--;
    }
  }
  return arr;
};

var item1 = {item: "item1"};
var data = [
  {item: "item1"},
  {item: "item_non"},
  {item: "item1"},
  {item: "item_non"},
  {item: "item1"},
  {item: "item1"},
  {item: "item1"},
  {item: "item_non"},
  {item: "item_non"}
];

console.log(filterArray(data, item1));
Geuis
  • 41,122
  • 56
  • 157
  • 219
-1

Here's my version http://codepen.io/anon/pen/VvQZVe?editors=001

Note I cleaned up the code a bit.

Repeatedly using indexOf in a loop is expensive. The worst case for doing that in a big array is having to go to almost the end of the loop many times.

This version potentially uses almost 2x the memory (imagine a big array where there's only a single item to be removed). But, its faster.

// indexOf is expensive, especially in a loop.
// here we just iterate straight through the array and append
// mismatching properties to another array that gets returned.
var filterArray = function (arr, obj) {
  var returnArray = [];

  arr.forEach(function (el) {
    if (el.item !== obj.item) {
      returnArray.push(el);
    }
  });

  return returnArray;
};

var item1 = {item: "item1"};
var data = [
  {item: "item1"},
  {item: "item_non"},
  {item: "item1"},
  {item: "item_non"},
  {item: "item1"},
  {item: "item1"},
  {item: "item1"},
  {item: "item_non"},
  {item: "item_non"}
];

console.log(filterArray(data, item1));
Geuis
  • 41,122
  • 56
  • 157
  • 219
  • 1
    Your `filterArray` function is equivalent to `Array.prototype.filter`, which he said won't work for him. – Barmar Oct 21 '15 at 23:33
  • He said he can't use Array.filter. He doesn't say anything about not being able to use a function that does something like a polyfill. – Geuis Oct 21 '15 at 23:40
  • He said "I need to delete the objects". I think that means that he needs to keep the same array, not create a new array like `filter` does. – Barmar Oct 21 '15 at 23:42