Remove array item using for...of loop - javascript

I'm trying to edit an array and remove elements that do not meet a certain condition. If I use a reverse for loop combined with .splice(index,n), the code works just fine. I'm stuck at implementing the same using the ES6 for...of loop
let array=[1,2,3];
//reverse for loop
for(var i=array.length - 1; i>=0; i--) {
if(array[i]>=2) {
/*Collect element before deleting it.*/
array.splice(i,1);
}
}
console.log(array);
// returns [1]
Using for..of
let array=[1,2,3];
for(let entry of array) {
if(entry>=2) {
let index = array.indexOf(entry);
/*The array is being re-indexed when I apply
.splice() - the loop will skip over an index
when one element of array is removed*/
array.splice(index, 1);
}
}
console.log(array);
//returns [1,3]
Is there a way to achieve this functionality using a for...of loop or do I have to stick to the reverse for loop
Update
I need to collect the entries that don't meet the elements removed by either the filter() or reverse for loop functions to a secondary array.

You can't reasonably use for-of for this. If you remove the "current" entry during the iteration, you'll skip the next entry, because of the way array iterators are specified (they use the index of the entry). You can see that with this contrived example:
const array = [1, 2, 3];
for (const entry of array) {
console.log(entry);
if (entry === 2) {
array.splice(1, 1);
}
}
console.log(array);
Notice how there was no loop iteration for entry 3.
I'd suggest either sticking with your reverse for loop or using filter to produce a new array containing only the entries you want to keep.
Using filter:
let array = [1, 2, 3];
array = array.filter(entry => entry < 2);
console.log(array);
I said "reasonably" above because, of course, there's always a way. You could loop through a copy of the array and maintain the index outside it:
const array = [1, 2, 3];
let index = 0;
for (const entry of [...array]) {
if (entry >= 2) {
array.splice(index, 1);
} else {
++index;
}
}
console.log(array);
That doesn't seem reasonable compared to the alternatives, unless of course there are constraints pushing you that way. :-)

According to #T.J's answer, when using a for...of loop:
If you remove the "current" entry during the iteration, you'll skip the next entry, because of the way array iterators are specified (they use the index of the entry).
This leaves two other options, using a reverse for loop and a filter function. I mentioned earlier that I need to do an operation with the current array element before deleting it.
1. Using .filter() function
and referring to #T.J's Comment
let array = [1,2,3];
let collection = [];
array = array.filter(function(entry) {
if(entry>=2) {
collection.push(entry);
}
return entry <2;
});
console.log(collection); //returns [2,3]
console.log(array); //returns [1]
2. Using a reverse for loop
let array = [1,2,3];
let collection = [];
for(var i=array.length - 1; i>=0; i--) {
if(array[i]>=2) {
collection.push(array[i]);
array.splice(i,1);
}
}
console.log(collection); //returns [2,3]
console.log(array); //returns [1]
The filter() function in this case requires an additional step to temporarily hold the elements that do not meet the condition. The reverse for loop offers a more cleaner way to achieve the same.

iterator skips the next entry on removing current entry in the array. So you should this way to achieve your desired result.
let array = [1,2,3];
let array_new = [1,2,3];
for(let entry of array) {
if(entry>=2) {
let index = array_new.indexOf(entry);
array_new.splice(index, 1);
}
}
console.log(array_new);
//returns [1]

Related

Possible to push empty slot to an array?

I'm building my own map method to be as close as the native map method.
Since the native map pushes(i think) the changed values into a new array, it still keeps the empty slots. I wasn't able to find a solution to push an empty slot into an array, like this example below.
[1, 2, 3].push(some code) // [1, 2, 3, empty]
I tried pushing an array with one empty item prefixed with a spread operator arr.push(...(new Array(1))) or arr.push(...[,]) but that just pushes undefined.
I solved my problem by not using push and instead assigning values to the array index that way skipped indices will be set to empty.
But I'm writing this post to see if anyone knows that if it's possible to use the push method to push an empty slot to an array.
No, it's not possible, not with the push method. empty can only exist if the array has a certain length, but a whole number property of the array does not exist at some index. This is called a sparse array, and cannot be created with push (or other array methods, if they're called on and with non-sparse arrays).
The only way to do so would be to assign to an index for which a lower index doesn't exist yet.
Look at the results for the below two snippets in your browser console, not the snippet console:
const arr = [];
arr[1] = 'a';
console.log(arr);
Or to set the .length of the array above the last index that the array has:
const arr = [];
arr.length = 1;
console.log(arr);
But the two approaches above are very weird to do and probably have no good reason to be used. Better to avoid sparse arrays entirely.
Keep in mind that an empty slot is different from undefined, which is perfectly possible to have as an array value:
const arr = [];
arr.push(undefined);
console.log(arr);
You can create an empty slot in an array by incrementing the array length:
var a = []
a.push(1)
a.length++
a.push(3)
console.log(a)
console.log(1 in a) // anything at index 1?
Alternatively, you can push something and then delete it:
var a = []
a.push(1)
a.push(2)
a.push(3)
delete a[1]
console.log(a)
console.log(1 in a) // anything at index 1?
There is no need to actually push to a new array in your implementation. You can simply do new Array(this.length) where this.length is the array you are mapping through length.
For example consider this map implementation:
if (!Array.prototype.mapIt) {
Object.defineProperty(Array.prototype, "mapIt", {
value: function(fn) {
if (this === null) {
throw new TypeError('Array.prototype.mapIt called on null or undefined');
}
if (typeof fn !== 'function') {
throw new TypeError('predicate must be a function');
}
let _array = this.filter(x => x != null) // remove empty values
let result = new Array(_array.length) // the new array we will return
for (var i = 0; i < _array.length; i++) {
result[i] = fn.call(arguments[1], _array[i], i, _array) // call the predicate
}
return result;
}
});
}
let arr = [1, 2, , , 3] // the test array
let result = arr.mapIt((c, i, a) =>
console.log(`current: ${c}`, `index: ${i}`, `array: ${a}`) || c + 2)
console.log('result: ', result)
console.log('original array: ', arr)
Hope this helps you with an gives you an idea about a possible map implementation.

add item to array in loop by this array js

I have the following method:
var items = [1,2,3];
$.map(items, function (item) {
if (item === 1) {
items.push(4);
}
console.log(item);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
and I expect in console 1,2,3,4, but see 1,2,3. I mean I want to see one extra loop item.
Can I resolve it somehow? And if yes, how can I resolve it?
Iterator methods, like .map() or .forEach(), will prevent visiting elements added during iteration by using only the original length.
To avoid that, you'll want to use a standard loop, such as for..of (with the default array iterator checking length as it progresses):
var items = [1, 2, 3];
for (var item of items) {
if (item === 1) {
items.push(4);
}
console.log(item);
}
Though, other types of loops can be used to do the same.
Of course, beyond this current example, be careful that the loop doesn't become infinite from there always being new elements to iterate to next.
Yes certainly you can resolve it, but in your specific case, items is passed by value to your map function so that you won't accidentally alter the original variable. The purpose of map is not for what you are using, but for mapping by specific key for an object or associative array. You should fall back to for loop or some other method for getting your desired output.
var items = [1, 2, 3];
for (var i = 0; i < items.length; i++) {
const item = items[i];
if (item === 1) {
items.push(4);
}
console.log(item);
};
use forIn instead of forOf, because sometime forOf give an error (maybe forOf not supported older version of js )..
var items = [1,2,3];
var k;
for (k in items){
if(items[k] === 1){
items.push(4);
}
}
alert(items);

Typescript way to slice elements from array while looping over it

What is the typescript way to slice elements from an array while looping over all its elements? Obviously, I want to take into account that the array is re-indexed at every splice.
As stated in this post the javascript way of doing this would be:
var i = array.length
while (i--) {
...
if (...) {
array.splice(i, 1);
}
}
Unfortunately, the typescript for (let item of array) {} loops from 0 to array.length-1 instead of the other way around like the js function above. Splicing an element in this loop would result in one item being skipped.
From what I understood from your answers what you need is to filter the array:
const filteredArray = array.filter(element => {
if (yourConditionIsTrue) { // if this element should be in the filteredArray
return true;
} else {
return false
}
});
Which can be done in one line:
const filteredArray = array.filter(element => conditionIsTrue);
This way your array remains untouched and you get a new array (filteredArray) only with the elements you need, but you don't mess up with the array you are iterating.

Remove and store an array item without creating garbage

I'm looking for a performant way to remove and store elements from an array. I am trying to make an object pool to reduce garbage collections calls.
Just as .pop() and .unshift() remove elements from an array and return the value of that element, I'd like to be able to remove an element at a specific index, while storing it's value in a variable, and while not creating unnecessary arrays/objects.
.splice() removes the element at a specific index just fine, and stores that value in an array. I can access that, but the function itself creates a new array, which will eventually trigger the garbage collector.
.slice() has the same issue, a new array is created.
Is there a way to pull out and store a specific indexed element without the creation of a new array?
This always removes one item at index, if you need to remove more than 1 consecutive items at a time, it would be
more efficient to implement it to take a howMany argument and remove them in a batch instead of calling
removeAt repeatedly.
function removeAt(array, index) {
// Assumes array and index are always valid values
// place validation code here if needed
var len = array.length;
// for example if index is not valid here, it will deoptimize the function
var ret = array[index];
for (var i = index + 1; i < len; ++i) {
array[i - 1] = array[i];
}
array.length = len - 1;
return ret;
}
Usage:
var a = [1,2,3,4,5]
var removed = removeAt(a, 2);
console.log(a);
// [1, 2, 4, 5]
console.log(removed);
// 3

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?
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"])

Categories

Resources