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?
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.
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.
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.
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);
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.
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]
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);
}
});
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;
}
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])));
}
}
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);
}
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"])
Related
I have an array that has zeroes in random indexes. The indexes for given different arrays are unknown. I used the following for loop to find and remove the zeros, so the resulting array is the same one without the zeros.
for (let i=0; i< arr.length; i++){
if(arr[i] === 0){
itemIndex = arr.indexOf(arr[i]);
arr.splice(itemIndex, 1); //removes the zero at the index
i = 0; //resets the i to zero so that every item in the array is searched even after it is altered.
}
}
As you can see, it resets the "i" to zero so that I can iterate through the array again because it will be altered and indexes of the zeroes will change. I am wondering if there is a better way to do this? I have a feeling that this could be coded better.
You only need to decrement i by 1. Also, there is no need to use indexOf when you already have the index.
for (let i=0; i< arr.length; i++){
if(arr[i] === 0){
arr.splice(i, 1); //removes the zero at the index
i--;
}
}
Looping backwards also solves the issue without additional counter manipulation, as only elements that have already been checked will be shifted.
Of course, it is much easier to use Array#filter instead.
arr = arr.filter(x => x !== 0);
Here's a couple options you can take here...
Retain your for loop: instead of i = 0, you could decrement i instead (i.e. replace i = 0 with i--)
Replace the loop with a filter: arr = arr.filter(element => element !== 0)
Both will modify the array in place, like you wanted. The second option will not work if arr was initialized as a constant. If that's the case, you'll have to go with option 1, or assign the filter result to a new array and use that after the filter in place of your original array.
Second option is a bit cleaner, but the first is a small change to your existing code to have it to work as you expected.
You could count down (i.e. for (let i=arr.length-1; i>=0; i--) so the indices of the elements you haven't checked yet won't change.
let result = arr.filter(e => e!==0)
Or if you want to stick with the for loop implementation :
what about populating a new array instead of mutating the one you are looping over:
let result = []
for(let e of arr) {
if(e!==0) result.push(e)
}
This is my preferred method of removing items from an array like this.
const newArray = array.filter(item => ele !== 0)
I have an array of objects and I am removing elements based on object value, without knowing which element it is.
So... those lovely people who flagged my question as duplicate and suggest I read How do I remove a particular element from an array in JavaScript? are not helping.
My question... again...
I have an array of objects
I want to search and remove specific elements from the array. I have a solution that is long winded (loop thru the array once, make a list of element id's, then slice one by one in another loop). I am hoping there is another method (enhance my skillset too).
myarray.filter(function(item)
{
if( item.flag==="Y" )
{
// how to delete element from myarray
// delete implies remove it from myarray
}
});
All help appreciated =)
You have at least two options:
Create a new array with filter
The first is to create a new array containing only the entries you want. You're already on the right track with filter:
myArray = myArray.filter(function(item) {
return item.flag !== "Y";
});
The new array will only have the entries that the callback returned a truthy value for. In this case, only ones whose flag is !== "Y". Entries whose flag is === "Y" will be left out of it.
Note that if you have other references to this same array in other variables, they will continue to point to the old array. Only myArray (in the above) gets a reference to the new array.
Modify the existing array with splice and a loop
The second is to use a while loop and splice:
var n = 0;
while (n < myArray.length) {
if (myArray[n].flag === "Y") {
// Remove it
myArray.splice(n, 1);
} else {
// Move to next
++n;
}
}
Note we don't move to next if we remove one, because we need to test the one after it (which is now at the previous index).
Or as Nina points out in a comment, a for loop going backward:
for (var n = myArray.length - 1; n >= 0; --n) {
if (myArray[n].flag === "Y") {
// Remove it
myArray.splice(n, 1);
}
}
In that case, since we're modifying the array (not creating a new one), the changes will be visible through any reference to that same array, not just myArray. (Since they all point to the same object, which we modified the state of.)
Remove object in array by its property-value:
Remove value: 30:
arr.splice(arr.map(el => el.value).indexOf(30), 1);
DEMO
Remove name: "John":
arr.splice(arr.map(el => el.name).indexOf("John"), 1);
Let's say you have the follow array:
var array = ["app_angrybirds", "app_flappybird", "ilovebirds"];
How would you go about removing any array element that begins with the string "app_"?
for (var i = 0; i < array.length; i++) {
if(array[i].substr(0, 4) === "app_") {
array.splice(i, 1);
i--; //The index will be moved one forward at the end of the loop, but the whole array
//will be shifted, so we compensate by decrementing i
}
}
You can just loop through and check the first 4 characters (using substr) and compare it. If they match, remove the element using splice. We also use a small hack using i-- to keep the index pointing at the right place.
Explanation for the above:
The for loop steps through each element of the array. For each iteration, you compare the first four characters of the element,whose index matches with the iteration variable, to the string "app_". Here the array method substr(0,4) isolates the characters from the 0th element up to but not including the 4th element and makes the substring available for comparison with "app_".If the condition is true that the substring of the element matches with "app_" , use the splice() method to surgically remove the element from the array based on the parameters you pass to it, in this case the ith element.
This is the simplest way that I can think of:
var array = ["app_angrybirds", "app_flappybird", "ilovebirds"];
var result = array.filter(doesNotHave(/^app_/));
alert(JSON.stringify(result));
function doesNotHave(regex) {
return function (str) {
return !regex.test(str);
}
}
Hope that helps.
I need to remove all elements in an array that do not contain "IN" in uppercase exactly like that.
How I thought of doing this was to traverse the array with a for loop and write all values that contain IN to another array.
Is there a way I can do it without writing to a new array and just removing those items that don't match from the current array?
Here is the code for how I was planning on doing it:
arrTwo = [];
for(var i = 0; i<arr.length; i++){
if(arr[i].indexOf('IN') > -1) arrTwo.push[arr[i]];
}
You can use ES5 filter method:
arr = arr.filter(function(s){
return ~s.indexOf("IN");
});
And using ES6 arrow functions, it can be simplified to:
arr = arr.filter(s=>~s.indexOf("IN"));
Here's a really good thread that has a couple of ways to accomplish this. If you do not delete the element of the array in the correct manner, you that element will be undefined rather than actually deleted. The .spilce() method is what you want to look into.
Deleting array elements in JavaScript - delete vs splice
I would do it using the splice() method:
var testArray = [ 'this one contains IN', 'this one does not' ];
function filterArray ( arr ) {
var i = arr.length;
//-- Loop through the array in reverse order since we are modifying the array.
while (i--) {
if (arr[i].indexOf('IN') < 0) {
//-- splice will remove the non-matching element
arr.splice(i, 1);
}
}
}
filterArray( testArray );
document.body.innerText = JSON.stringify(testArray);
JSFiddle: http://jsfiddle.net/5DW8L/1/
I have this code to iterate through an array of objects:
for (vehicleIndex in scenes[sceneID].vehicles) {
vehicleID = scenes[sceneID].vehicles[vehicleIndex];
...
}
but I need to know how to determine the number of items being iterated through so that on the final item, I can execute a particular function. How do I do this?
Example in ES5:
Object.keys( scenes[sceneID].vehicles ).forEach(function( vehicle, index, arr ) {
if( index === arr.length - 1 ) {
// do something on last entry
}
});
Even tho, "last" just means the last element which was looped over. Since there is specific order within a Javascript object (they are infact unordered). However, we could sort the object key names manually by just chaining a .sort() before the .forEach()
var arraySize = scenes[sceneID].vehicles.length;
var i;
var currentItem;
for (i = 0; i < arraySize; i++) {
currentItem = scenes[sceneID].vehicles[i];
if (i == arraySize - 1) {
// do something special
} else {
// do something not so special ;-)
}
}
scenes[sceneID].vehicles should have a length property.
for (vehicleIndex in scenes[sceneID].vehicles) {
vehicleID = scenes[sceneID].vehicles[vehicleIndex];
...
}
doSomethingWithLastItem(vehicleId);
Because JS does not have block scope, by the time your loop finished vehicleId will be the last item's id.
In generic terms you can get the size of an array by accessing the .length property. You can also get the size of an object using Object.keys(obj).length.
Use this to find the length:
scenes[sceneID].vehicles.length
length is a built-in property in arrays. However, if you want to check for the last item, you have to check
scenes[sceneID].vehicles.length - 1
as arrays are zero-indexed.
Also, you should not use for...in to loop on arrays - if someone extends Array.prototype (or worse, Object.prototype), then you will not be happy. Use a normal for loop (which also allows you to use the final item easily):
var len = scenes[sceneID].vehicles.length;
for (var vehicleIndex = 0; vehicleIndex < len; vehicleIndex++) {
vehicleID = scenes[sceneID].vehicles[vehicleIndex];
//...
}
//Do something with the final item here
//Similar to this: itemFunc(vehicleID);
See this SO question for more details.