Reputation: 339
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
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
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
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
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:
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
Upvotes: 38
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
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
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
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
Reputation: 16526
Create a set given an array, the original array is unmodified
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
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
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