Eduen Sarceno
Eduen Sarceno

Reputation: 339

Loop to remove an element in array with multiple occurrences

I want to remove an element in an array with multiple occurrences with a function.

var array=["hello","hello","world",1,"world"];

function removeItem(item){
    for(i in array){
        if(array[i]==item) array.splice(i,1);
    }
}
removeItem("world");
//Return hello,hello,1
removeItem("hello");
//Return hello,world,1,world

This loop doesn't remove the element when it repeats twice in sequence, only removes one of them.

Why?

Upvotes: 23

Views: 24686

Answers (11)

jsBug
jsBug

Reputation: 355

I thinks this code much simpler to understand and no need to pass manually each element that what we want to remove

ES6 syntax makes our life so simpler, try it out

const removeOccurences = (array)=>{
const newArray= array.filter((e, i ,ar) => !(array.filter((e, i ,ar)=> i !== ar.indexOf(e)).includes(e)))
console.log(newArray) // output [1]
}
removeOccurences(["hello","hello","world",1,"world"])

Upvotes: 0

Utkarsh Dhawan
Utkarsh Dhawan

Reputation: 324

You can use the following piece of code to remove multiple occurrences of value val in array arr.

while(arr.indexOf(val)!=-1){
  arr.splice(arr.indexOf(val), 1);
}

Upvotes: 0

wolfsbane
wolfsbane

Reputation: 1827

An alternate approach would be to sort the array and then playing around with the indexes of the values.

function(arr) {
    var sortedArray = arr.sort();
    //In case of numbers, you can use arr.sort(function(a,b) {return a - b;})
    for (var i = 0; sortedArray.length; i++) {
        if (sortedArray.indexOf(sortedArray[i]) === sortedArray.lastIndexOf(sortedArray[i]))
            continue;
        else
            sortedArray.splice(sortedArray.indexOf(sortedArray[i]), (sortedArray.lastIndexOf(sortedArray[i]) - sortedArray.indexOf(sortedArray[i])));
    }
}

Upvotes: 0

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276586

You have a built in function called filter that filters an array based on a predicate (a condition).

It doesn't alter the original array but returns a new filtered one.

var array=["hello","hello","world",1,"world"];
var filtered = array.filter(function(element) {
    return element !== "hello";
}); // filtered contains no occurrences of hello

You can extract it to a function:

function without(array, what){
    return array.filter(function(element){ 
        return element !== what;
    });
}

However, the original filter seems expressive enough.

Here is a link to its documentation

Your original function has a few issues:

  • It iterates the array using a for... in loop which has no guarantee on the iteration order. Also, don't use it to iterate through arrays - prefer a normal for... loop or a .forEach
  • You're iterating an array with an off-by-one error so you're skipping on the next item since you're both removing the element and progressing the array.

Upvotes: 38

Rahul Arora
Rahul Arora

Reputation: 4543

You can use loadash or underscore js in this case if arr is an array you can remove duplicates by:

var arr = [2,3,4,4,5,5];

arr = _.uniq(arr);

Upvotes: 1

Daniel Gimenez
Daniel Gimenez

Reputation: 20609

None of these answers are very optimal. The accepted answer with the filter will result in a new instance of an array. The answer with the second most votes, the for loop that takes a step back on every splice, is unnecessarily complex.

If you want to do the for loop loop approach, just count backward down to 0.

for (var i = array.length - 0; i >= 0; i--) {
  if (array[i] === item) {
    array.splice(i, 1);
  }
}

However, I've used a surprisingly fast method with a while loop and indexOf:

var itemIndex = 0;
while ((itemIndex = valuesArray.indexOf(findItem, itemIndex)) > -1) {
  valuesArray.splice(itemIndex, 1);
}

What makes this method not repetitive is that after the any removal, the next search will start at the index of the next element after the removed item. That's because you can pass a starting index into indexOf as the second parameter.

In a jsPerf test case comparing the two above methods and the accepted filter method, the indexOf routinely finished first on Firefox and Chrome, and was second on IE. The filter method was always slower by a wide margin.

Conclusion: Either reverse for loop are a while with indexOf are currently the best methods I can find to remove multiple instances of the same element from an array. Using filter creates a new array and is slower so I would avoid that.

Upvotes: 2

JayCrossler
JayCrossler

Reputation: 2129

I needed a slight variation of this, the ability to remove 'n' occurrences of an item from an array, so I modified @Veger's answer as:

function removeArrayItemNTimes(arr,toRemove,times){
    times = times || 10;
    for(var i = 0; i < arr.length; i++){
        if(arr[i]==toRemove) {
            arr.splice(i,1);
            i--; // Prevent skipping an item
            times--;
            if (times<=0) break;
        }
    }
    return arr;
}

Upvotes: 0

Amir Rahnama
Amir Rahnama

Reputation: 6214

I must say that my approach does not make use of splice feature and you need another array for this solution as well.

First of all, I guess your way of looping an array is not the right. You are using for in loops which are for objects, not arrays. You'd better use $.each in case you are using jQuery or Array.prototype.forEach if you are using vanila Javascript.

Second, why not creating a new empty array, looping through it and adding only the unique elements to the new array, like this:

FIRST APPROACH (jQuery):

 var newArray = [];
 $.each(array, function(i, element) {
        if ($.inArray(element, newArray) === -1) {
            newArray.push(region);
        }
 });

SECOND APPROACH (Vanila Javascript):

var newArray = [];
array.forEach(function(i, element) {
  if (newArray.indexOf(element) === -1) {
            newArray.push(region);
  }
});

Upvotes: 0

robbmj
robbmj

Reputation: 16526

Create a set given an array, the original array is unmodified

Demo on Fiddle

    var array=["hello","hello","world",1,"world"];

    function removeDups(items) {
        var i,
            setObj = {},
            setArray = [];
        for (i = 0; i < items.length; i += 1) {
            if (!setObj.hasOwnProperty(items[i])) {
                setArray.push(items[i]);
                setObj[items[i]] = true;
            }
        }
        return setArray;
    }

    console.log(removeDups(array)); // ["hello", "world", 1]

Upvotes: 0

Veger
Veger

Reputation: 37915

That is because the for-loop goes to the next item after the occurrence is deleted, thereby skipping the item directly after that one.

For example, lets assume item1 needs to be deleted in this array (note that <- is the index of the loop):

item1 (<-), item2, item3

after deleting:

item2 (<-), item3

and after index is updated (as the loop was finished)

item2, item3 (<-)

So you can see item2 is skipped and thus not checked!

Therefore you'd need to compensate for this by manually reducing the index by 1, as shown here:

function removeItem(item){
    for(var i = 0; i < array.length; i++){
        if(array[i]==item) {
            array.splice(i,1);
            i--; // Prevent skipping an item
        }
    }
}

Instead of using this for-loop, you can use more 'modern' methods to filter out unwanted items as shown in the other answer by Benjamin.

Upvotes: 7

Guy Dafny
Guy Dafny

Reputation: 1829

Try to run your code "manually" - The "hello" are following each other. you remove the first, your array shrinks in one item, and now the index you have follow the next item.

removing "hello"" Start Loop. i=0, array=["hello","hello","world",1,"world"] i is pointing to "hello" remove first item, i=0 array=["hello","world",1,"world"] next loop, i=1, array=["hello","world",1,"world"]. second "hello" will not be removed.

Lets look at "world" = i=2, is pointing to "world" (remove). on next loop the array is: ["hello","hello",1,"world"] and i=3. here went the second "world".

what do you wish to happen? do you want to remove all instances of the item? or only the first one? for first case, the remove should be in

while (array[i] == item) array.splice(i,1);

for second case - return as soon as you had removed item.

Upvotes: 0

Related Questions