So here are two fiddles:
Fiddle 1
Fiddle 2
When using forEach and then array shift inside the loop the output results in duplicates but if I use a regular for loop and use the shifted return as output it works correctly.
So the only difference between the two are:
var queueSize = testArray.length;
if (queueSize > 0) {
testArray.forEach(function (loopData) {
jQuery('.js-target').append(loopData+'<br>');
testArray.shift();
});
}
Compared to:
for (var i = 0; i < testArray.length; i++) {
var d = testArray.shift();
jQuery('.js-target').append(d+'<br>');
}
To me the first one should work just as well as the second one. (The data is added to testArray with push i.e. to the end of the array). Is it something I'm not understanding with the shift function?
The shift method will remove the element at the start of the array. So your array in the second example is being shortened at each iteration.
The shift method removes the element at the zeroeth index and shifts
the values at consecutive indexes down, then returns the removed
value. If the length property is 0, undefined is returned.
See the MDN docs for a full overview.
You may want to read this explanation in MDN it's really nice
The following example logs "one", "two", "four". When the entry containing the value "two" is reached, the first entry of the whole array is shifted off, which results in all remaining entries moving up one position. Because element "four" is now at an earlier position in the array, "three" will be skipped. forEach() does not make a copy of the array before iterating.
var words = ['one', 'two', 'three', 'four'];
words.forEach(function(word) {
console.log(word);
if (word === 'two') {
words.shift();
}
});
// one
// two
// four
If you change the elements of the array (as opposed to their values) during a forEach loop, it is going to behave differently to a regular for loop.
From MDN:
The range of elements processed by forEach() is set before the first
invocation of callback. Elements that are appended to the array after
the call to forEach() begins will not be visited by callback. If the
values of existing elements of the array are changed, the value passed
to callback will be the value at the time forEach() visits them;
elements that are deleted before being visited are not visited.
Related
I have an algorithm :
let someArray = [1, 2, 3, 4, 5]
let mapped = someArray.map(number => {
let index = someArray.indexOf(5)
if (index !== -1) {
someArray.splice(index, 1)
}
console.log(typeof number)
return number
})
console.log(mapped)
console.log(mapped.length)
console.log(Object.keys(mapped).length)
So what I expected to have was mapped=[1,2,3,4] and mapped.length=4
But instead I have mapped=[1,2,3,4,empty] and mapped.length=5.
So what I thought is : in the beginning, map is going for 5 iterations so it does it no matter what. That's why I added console.log(typeof number).
But it's executed only 4 times.
I know to have my expected result, filter is way better. I'm just wondering, what is happening here ?
See MDN documentation:
map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values, including undefined. It is not called for missing elements of the array (that is, indexes that have never been set, which have been deleted or which have never been assigned a value).
If existing elements of the array are changed, their value as passed to callback will be the value at the time map visits them. Elements that are deleted after the call to map begins and before being visited are not visited.
You're mutating the array as you're iterating over it, which means that once index [4] is reached, that element (whose value used to be 5) doesn't exist anymore, which means the function does not get called on that iteration, resulting in <empty>. The resulting array is created with 5 elements, but the callback is never called on the last element.
Use filter instead.
Is this intended behavior? I would expect an empty array to be returned here.
JavaScript
let arr = [1];
console.log(arr.splice(0, 1))
Console
1
Because it returns what was removed, which is [1] in your case. arr will be empty after the call.
See example:
let arr = [1];
arr.splice(0, 1);
console.log(arr);
Array.prototype.splice() returns an array of the deleted elements.
Your are logging the returned value of the operation not the current state of the array.
The splice() method changes the contents of an array by removing
existing elements and/or adding new elements.
Reference
The syntax of this method is:
array.splice(start)
array.splice(start, deleteCount)
array.splice(start, deleteCount, item1, item2, ...)
You are using the second syntax and passing 0 as start and 1 as deleteCount that means you would like to start at 0th index and delete 1 item (not 1 as value in array).
This method returns an array containing the deleted elements. If only one element is removed, an array of one element is returned. If no elements are removed, an empty array is returned.
So, in this case the result is [1] i.e. an array with value of 1 that is the item you just deleted.
I ran into this question when I was trying to achieve a function in mini-program. A solution is to check the array's length before using the function splice. For example:
if(imgList.length > 1){
imgList.splice(index, 1);
else{
imgList = []
}
You can try to solve it like that.
I have two identical arrays: itemsOutput& itemsOutput2
I want to delete those objects in the arrays with attributes.type = "DIMENSION". I have found two different methods for doing so:
Method 1
jQuery.each(itemsOutput, function(i, val) {
if(val.attributes.type == "DIMENSION") // delete index
{
delete itemsOutput[i];
}
});
console.log(itemsOutput.length);
Method 2
metrics = itemsOutput2.filter(function (el) {
return el.attributes.type === "METRIC";
});
console.log(metrics.length);
Although both new arrays seem to have the same number of objects (and in both, all objects with attributes.type = "DIMENSION" are gone), the console shows two totally different values for the length of each array.
Method 1 removes the objects, but length is the same as the original array (although exploring the array in the console I observe that the objects keep their original indexes)
Method 2 not only removes the objects, but it also reassings the indexes successively. And for me, the code seems more clear, so I will stay with this method.
My question is, however, why this happens and if there could be problems if I use the results of method 1 in a loop, for example.
When you delete an array element, the array length is not affected. This holds even if you delete the last element of the array. When the delete operator removes an array element, that element is no longer in the array but the length stays the same.
You will need to use method splice if you want to remove it from the array. For example:
itemsOutput.splice(i, 1);
I haven't got any code yet, as I was just wondering if it is possible to loop through an Array that is dynamically populated to the amount of values could be different each time. The variables would obviously have to make use of an incrementing value?
You can use the array length property to work with an unknown array lengths:
var arr = ["carrots", "bananas", "onions"];
for (var i = 0, len = arr.length; i < len; i++) {
//every element accesible via arr[i];
//example:
console.log(arr[i]);
}
That will loop through the whole array even if it there are more or less elements on it
With this base, I am sure you can do what you want from here
I'm sure by now you've probably figured it out, but I'll just add this here for any future Javascript beginners.
Javascript Arrays have a built-in function called forEach allowing you to iterate over every element in an array. It functions as a loop specifically meant for Arrays. It takes a callback function as an argument and looks like the following:
let stringArr = ['dog', 'cat', 'lion', 'elephant'];
stringArr.forEach(function(element, counter) {
console.log(counter+') '+element);
});
// Outputs:
0) dog
1) cat
2) lion
3) elephant
The callback function can, of course be replaced by an arrow function, if you'd like, but as you can see the first argument in the callback function (element) is the element in the array, and the second argument (counter) keeps track of the index of the element in the array.
What I love about the forEach is that it makes accessing the array elements easier (even if only somewhat) than using a standard for-loop. For instance when looping through objects with a for-loop, to access the individual elements in the array one would have to do myArray[i].property, but using the forEach, one can simply do element.property.
Here's some additional reading on the forEach if it still hasn't quite clicked yet.
I want to iterate over two arrays at the same time, as the values for any given index i in array A corresponds to the value in array B.
I am currently using this code, and getting undefined when I call alert(queryPredicates[i]) or alert(queryObjects[i]).
I know my array is populated as I print out the array prior to calling this.
//queryPredicates[] and queryObjects[] are defined above as global vars - not in a particular function, and I have checked that they contain the correct information.
function getObjectCount(){
var variables = queryPredicates.length; //the number of variables is found by the length of the arrays - they should both be of the same length
var queryString="count="+variables;
for(var i=1; i<=variables;i++){
alert(queryPredicates[i]);
alert(queryObjects[i]);
}
The value of the length property of any array, is the actual number of elements (more exactly, the greatest existing index plus one).
If you try to access this index, it will be always undefined because it is outside of the bounds of the array (this happens in the last iteration of your loop, because the i<=variables condition).
In JavaScript the indexes are handled from 0 to length - 1.
Aside of that make sure that your two arrays have the same number of elements.
If queryPredicates does not have numerical indexes, like 0, 1, 2, etc.. then trying to alert the value queryPredicates[0] when the first item has an index of queryPredicates['some_index'] won't alert anything.
Try using a for loop instead:
stuff['my_index'] = "some_value";
for (var i in stuff)
{
// will alert "my_index"
alert(i);
// will alert "some_value"
alert(stuff[i]);
}
Arrays in JS are zero based. Length is the actual count. Your loop is going outside the bounds of the array.