Javascript: What is the use case for negative loops? - javascript

I normally use for loops which count positively.
var i;
for (i = 0; i < cars.length; i++) {
text += cars[i] + "<br>";
}
I am curious to know in what use case would counting backwards be used?

Say you have an array of 10 numbers.
const myArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
Your task is to delete the ones that are divisible by 2 and convert to a string with an a the others (for example 1 will be 1a). (For the sake of the argument let´s say no map or filter functions exist).
If you parse the array from the start for (i = 0; i < myArray .length; i++), everytime you delete an item with for example splice the array length changes and no all items will be correctly parsed.
BUT if you starts from the end this will be not a problem.

Here a example:
When you retrieve data from a database for example blogposts, the data will be most of the time an array with objects in it and render them via a loop on your website the last data that was inserted in the database will be display at the top. Maybe you want to show the oldest blogpost so you could just change your loop to count backwards.
I hope you get the point what I mean, that's just a example

If you were attempting to loop over items in any array-like object (using the .length of the array as your loop boundary) and delete them, you'd find that if you counted incrementally, the loop would not run the correct amount of times because the length would change each time you deleted an item:
// Get all the divs into a collection
let divs = document.getElementsByTagName("div");
// Set up a loop that will go as many times as there are items in the collection
for(var i = 0; i < divs.length; i++){
divs[i].remove(); // Remove the div being iterated from the page
}
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
But, if you remove the elements at the end of the collection and work backwards, the length is adjustment doesn't get out of sync with the contents of the collection:
// Get all the divs into a collection
let divs = document.getElementsByTagName("div");
// Set up a loop that will go as many times as there are items in the collection
// But count backwards to keep the length from causing issues.
for(var i = divs.length - 1; i > -1 ; i--){
divs[i].remove(); // Remove the div being iterated from the page
}
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>

Related

How can you dynamically slice an array in Javascript/jQuery?

I have a photo gallery that includes images that will be continuously uploaded. The PHP array has been converted/encoded to a JSON array so that I can manipulate the data with JavaScript.
Ideally, I would like to click a button ("Next Set" in the CodePen example) and load the next set (of 2) thumbnail images. This is in an effort to not load all of the images at once, which could be hundreds.
Problem: I cannot figure out how to dynamically slice the array on click (next 5 images). I can of course load, say, 2 at a time:
myArray.slice(0,2);
myArray.slice(3,5);
However, this will not work because images will be continuously added to the gallery. Furthermore, I would have to have too many sets of the above to keep slicing 5 out at a time.
I have tried:
Splitting the array into smaller arrays
for loops and $.each loops
I essentially need to be able to move the start and end index of the slice by (for example) 2 on click. Right now it just keeps slicing the same two images because the slicing is not dynamic.
Here is my CodePen
I don't think there's a way to do exactly what you want, but you can just keep track of where you were in the array and do a slice from there, like this:
var nextSet = myArray.slice(lastIndex, lastIndex + 2);
Replace your existing click() with this (including the declaration of lastIndex) to try it:
var lastIndex = 0
$('.button').click(function() {
var nextSet = myArray.slice(lastIndex, lastIndex + 2);
lastIndex += 2;
for (var i = 0; i < 2; i++) {
var li = $('<li/>').attr('role', 'menuitem').appendTo('.myList').append('<img src=' + nextSet[i] + '>');
}
});
Note that I've moved the slice() line outside the for loop. There's no need to slice a new array for every iteration.
Here's a CodePen using .slice().
An alternate method is to use to shift() to peel off the first item in the array with each iteration:
var nextItem = myArray.shift()
This is destructive though (it removes the item from the original array), so you'll need to make a copy of the original array first if you want to use it for anything else. Replace your click() with:
$('.button').click(function() {
for (var i = 0; i < 2; i++) {
var nextItem = myArray.shift();
var li = $('<li/>').attr('role', 'menuitem').appendTo('.myList').append('<img src=' + nextItem + '>');
}
});
Here's a CodePen using .shift().
your problem is simple i think. you do a slice and allways get back the same array
var array = [0,1,2,3,4,5];
let newArray1 = array.slice(0,2); // returns a new array
let newArray2 = array.slice(0,2); // returns the same new array
for(var i = 0; i < 2; i = i+2) {
result = array.slice(i, i+2);
console.log(result);
}

JavaScript - Looping array less than I should

I'm trying to loop an array which contains a list of elements returned by ClassName, but I can't loop all of them, because of the next situation:
var list = document.getElementsByClassName('class');
for (var i = 0; i < list.length; i++) {
var theClass = list[i].className; //once got list[i].
theClass = theClass.replace('class', '');
list[i].className = theClass; //twice got list[i].
}
If the size of the list is = 4, I just can loop two times, because I'm getting twice each position per loop. Do you know what I can do and why it happens? Thank you.
The data structure returned by getElementsByClassName is Array-like and dynamic based on the DOM. Once you replace the class on the list item in question, you end up losing an item per iteration.
To fix this, you can take a copy of the returned values first before operating on them, or work backwards.
Take a copy:
var list = document.getElementByClassName('class')
var realList = []
Array.prototype.push.apply(realList, list)
for (var i = 0; i < realList.length; i++) {
// do changes as you have already
}
Working backwards:
var list = document.getElementsByClassName('class')
for (i=list.length - 1; i >= 0; i--) {
// do changes to list[i]
}
Another poster briefly mentioned a while loop which also works, but then their answer disappeared (I don't want to take credit for this!):
var list = document.getElementsByClassName('class')
while (list.length != 0) {
// do changes to list[0]
}
If you write out what happens in your initial code, you can see the problem more clearly:
Iteration 1: i=0, list=[a,b,c,d], length = 4, list[i]=a
Iteration 2: i=1, list=[b,c,d], length = 3, list[i]=c
Before Iteration 3: list=[b,d], i=2, length = 2, loop breaks
Now writing out what happens when using the reverse loop:
Iteration 1: i=3, list=[a,b,c,d], length = 4, list[i]=d
Iteration 2: i=2, list=[a,b,c], length = 3, list[i]=c
Iteration 3: i=1, list=[a,b], length = 2, list[i]=b
Iteration 4: i=0, list=[a], length = 1, list[i]=a
All these solutions are variations on this solution of avoiding using i to reference the middle parts of the array-like result value of getElementsByClassName so that the dynamic nature of it is dealt with.

JavaScript remove all elements with name

I am trying to use JavaScript to remove all the elements with a certian name, but it is only removing the first one.
My code is:
var ele= document.getElementsByName("javascriptaudio");
for(var i=0;i<ele.length;i++)
{
ele[i].parentNode.removeChild(ele[i]);
}
Can anyone tell me what is wrong?
Thanks
I don't have enough rep to comment on Álvaro G. Vicario. The reason that it works is that the element is removed from ele when it is removed from the DOM. Weird.
The following code should work equally well:
var ele= document.getElementsByName("javascriptaudio");
len = ele.length;
parentNode = ele[0].parentNode;
for(var i=0; i<len; i++)
{
parentNode.removeChild(ele[0]);
}
Try removing them backwards:
var ele = document.getElementsByName("javascriptaudio");
for(var i=ele.length-1;i>=0;i--)
{
ele[i].parentNode.removeChild(ele[i]);
}
The problem is that removing elements from ele shifts indexes: if you have 5 items (0 to 4) and remove item 0 you then have 4 items ranging from 0 to 3 (4 becomes 3, 3 becomes 2, etc.); you should then remove item 0 but your i variable has already incremented to 1.
you need to save length of array outside the loop because it is evaluated with each pass if you place it inside loop criteria. That way you will have correct amount of iterations. Furthermore you need to delete first item in loop each time because with each pass you lose 1 item so you will be outside of range at the end of process.
var ele = document.getElementsByName("javascriptaudio");
var elementsCount = ele.length;
for(var i=0;i<ele.length;i++){
ele[0].parentNode.removeChild(ele[0]);
}
Try the following jQuery code:
$("[name=javascriptaudio]").remove();

Javascript (dynamic) insert into array, then shift all elements underneath +1

Didn't really found a solution to this for Javascript.
What I need; I want to insert an element into an array, but not really overwrite that element. Rather a 'dynamic' insert. Thus Insert element, then shift all elements underneath it by +1 index.
For instance:
I have an array "14S" "16S" "19S".
I know want to insert "15S".
The resulting array: "14S" "15S" "16S" "19S"
What i tried:
fullName = "15S"
low = 5;
cardsS[low] = fullName;
for (var i = low; i < cardsS.length; i++) {
cardsS[i + 1] = cardsS[i];
}
If you know the position you want to insert the element into:
Use the splice method. It's cheap and works exactly like you want. You can also insert multiple elements at once:
var strings = ["14S", "16S", "19S"];
strings.splice(1,0,"15S");
Result
"14S" "15S" "16S" "19S"
You should also use this solution if you don't want the array to be sorted in a specific way.
If you don't know the position you want to insert the element into:
You will have to resort to a push/sort combination, supplying your own sort algorithm (unless the standard sort is enough)
var strings = ["14S", "16S", "19S"];
strings.push("15S");
strings.sort(function(a, b){
if (a is less than b by some ordering criterion)
return -1;
if (a is greater than b by the ordering criterion)
return 1;
// a must be equal to b
return 0;
});
You can use Array.splice to insert a value:
var arr = ["14S","16S","19S"];
arr.splice(1,0,"15S");
// ^position after which to insert
// ^number of elements to delete (none here)
// ^value to insert ("15S" here)
// => arr is now ["14S","15S","16S","19S"]
If you don't know the position, you could use Array.indexOf to determine it:
var arr = ["14S","16S","19S"];
arr.splice((arr.indexOf('14S')>-1 && arr.indexOf(after)+1 || 0),0,"15S");
// ^use indexOf result if applicable or just insert
// (so, if no position, this turns into unshift ;)
You can create a method for it:
function arrayInsertAfter(array, after, value){
after = array.indexOf(after)>-1 && array.indexOf('14S')+1 || 0;
array.splice(after, 0, value);
return array;
}
// usage
var arr = arrayInsertAfter(["14S","16S","19S"],"14S","15S");
// => ["14S","15S","16S","19S"]
MDN link for Array.splice
you just need to use push() and then sort() functions :
var yourArray = ['14S', '16S', '19S'];
yourArray.push('15S');
yourArray.sort();
You want Array.splice.
This splices a new element at position 1.
arr.splice(1, 0, '155');
Fiddle
What you want is the splice function on the native array object.
var arr = [];
arr[0] = "14S";
arr[1] = "16S";
arr[2] = "19S";
arr.splice(2, 0, "15S");
console.log(arr.join());
The resulting array: 14S, 16S, 15S, 19S
If you do that, you will start at cardsS[5], which will have the value of "fullName".
The fact is that your cardsS as 4 values, so your array is 0 to 3. You can see that your array at position 4 is unbind.
Moreover, if you do cardsS[i + 1], you will be at position 6, which is unbind too.
What you have to do is to:
Check how many item you have in your array (Lenght, Count)
Create a new array with Lenght + 1
Check if your new item is > or < to your first item. If it is >, you have to add your first item, if not you have to add your new item
Do this until your array is full.
If you don't want to do like that, you can use Splice function, just check it, or use a sort function from javascript library.
Hope this can help you !
Try This:
fullName ="15S"
cardsS = ["14S", "16S", "19S"];
for (var k in cardsS)
{
if(parseInt(fullName) < parseInt(cardsS[k])){
cardsS.splice(k,0,fullName)
break;
}
}
:)
if you think is better:
fullName ="15S"
cardsS = ["14S", "16S", "19S"];
for (var k = 0; cardsS.length > k; k++)
{
if(parseInt(fullName) < parseInt(cardsS[k])){
cardsS.splice(k,0,fullName)
break;
}
}
In one of your comments you asked the difference between the splice method and the push+sort method.
Splice just cuts up your array and inserts/deletes values based on index.
Using above mentioned example:
var strings = ["14S", "16S", "19S"];
strings.splice(1,0,"15S");
This will literally place the "15S" on index 1. This is why there is a remark "If you don't know the position you want to insert the element into:"
In which case the push+sort method comes into play. At this point you don't have to go about counting your elements and making sure you place everything at the right location.
You literally just push your "15S" at the end of the array, and then have the sort() automatically sort everything for you.
This, granted that your array is indeed sortable. Some setups don't really allow you to sort your arrays (Like colors sorted by color in the rainbow ... you can't just sort them using sort()).
In this case KooiInc's response comes into play. At this point you know where your item needs to be. 15S comes after 14S, so you search for 14S, get that index, and use that index to splice.
At this point you still have to calculate yourself what element 15S will have to go after - in my rainbow example you'll have to actively remember where you want to 'insert' which color based on which colors are in your array at this moment.
In your example the values seem to be suited for normal sorting, so i would go with the Push+sort approach a few people mentioned.
var arr = [1,2,4,5,6];
var element = 3, pos=3;
for(i=arr.length-1; i>=pos-1; i--){
arr[i+1]=arr[i];
}
arr[pos-1]=element;
console.log(arr);

Create multiple arrays based on frequency of coordinates in an array

Using JavaScript, I'd like to split one big array of coordinates into smaller arrays based on coinciding points. I am not 100% sure how to write the following in code but it describes what I'm attempting to achieve:
Iterate through the array
var A = [(1,2)(1,3)(2,3)(9,10)(9,11)(10,11)];
Combine the pairs that contain any matching/identical coordinate points:
var B = (1,2)(1,3)(2,3)
var C = (9,10)(9,11)(10,11)
Combine the matching/identical points and create new, smaller arrays from the combinations in point #2
var D = [1,2,3]
var E = [9,10,11]
Can I get help please?
Working answer: http://jsfiddle.net/y3h9L/
OK, so if I understand the requirement A is a one-dimensional array that is assumed to have an even number of elements in x,y pairs.
A = [1,2, 1,3, 2,3, 9,10, 9,11, 10,11]
// output should be
[ [1,2,3], [9,10,11] ]
// but if you add an extra pair that links the two halves, say add 2,11
A2 = [1,2, 1,3, 2,3, 9,10, 9,11, 10,11, 2,11]
// then all are related so output should be
[ [1,2,3,9,10,11] ]
I've made no effort to pretty-up or optimise the following code, but it works:
// single dimensional array of x,y pairs
var A = [1,2, 1,3, 2,3, 9,10, 9,11, 10,11];
// create a working copy of A so that we can remove elements
// and still keep the original A intact.
var workingCopy = A.slice(0, A.length),
matchedPairs = [],
currentMatches,
finalCombinations = [],
x, y, i, j,
tempArray;
while (workingCopy.length > 0) {
currentMatches = [];
currentMatches.push([workingCopy.shift(),workingCopy.shift()]);
workingCopyLoop:
for (x=0,y=1; x < workingCopy.length;) {
for (i=0; i < currentMatches.length; i++){
if (workingCopy[x] === currentMatches[i][0]
|| workingCopy[y] === currentMatches[i][1]) {
currentMatches.push([workingCopy.shift(),workingCopy.shift()]);
// go back to the beginning of workingCopyLoop
x=0;
y=1;
continue workingCopyLoop;
}
}
x += 2;
y += 2;
}
matchedPairs.push(currentMatches);
}
for (i=0; i<matchedPairs.length; i++){
tempArray = [];
for (j=0; j<matchedPairs[i].length; j++) {
// I assume you have a new enough version of JS that you have Array.indexOf()
if (-1 === tempArray.indexOf(matchedPairs[i][j][0]))
tempArray.push(matchedPairs[i][j][0]);
if (-1 === tempArray.indexOf(matchedPairs[i][j][1]))
tempArray.push(matchedPairs[i][j][1]);
}
finalCombinations.push(tempArray);
}
for (i=0; i<finalCombinations.length; i++)
console.log(finalCombinations[i]);
// console.log shows that finalCombinations = [ [1,2,3], [9,10,11] ]
If it's not obvious how this works, follow it through with a debugger and/or pencil and paper.
I must say your question is rather unclear, but i think i got it.
In other words what you're saying is:
I have an array containing a bunch of numbers, logically they represent coordinates, it's not that the coordinates are subarrays inside the master array, is just looking them 2 by 2, but it's a linear array.
What you want is something that detects coordinates that are adjacent and generate a new array containing them.
After that you want to go thru the new arrays and generate new arrays containing unique-elements.
Well that's the question, now the answer. First, the second point depends on how far you want to go, i'm thinking it's anormal grid of x,y coordinates, but how adjacent you want to go? The following just applies to the inmediate adjacent, up to 8 points can be adjacent to a single point.
[1,1][2,1][3,1]
[1,2][2,2][3,2]
[1,3][2,3][3,3]
May that be a representation of the grid, if your master array has the [2,2] coordinate, you want to build an array that begins with that one and all adjacents you find, lets say like master array has [3,2], then you want to add it to the subarray of [2,2].
I'm really not writing the code i'm just gonna explain sorts of algorithm you could use.
To build the second point arrays, lets call them Adjacents Arrays (AA) you could:
First coordinate will always build the first AA
To find adjacents you will cycle thru the master array and perform an "Adjacency Check" to every coordinate which would be: second x == ( first x-1, x or x+1) AND second y == ( first y-1, y or y+1), if it passes then pop/push, if not... next.
In case you finish cycling thru the master array means that AA is complete, and you have to start a new AA with the next coordinate.
Repeat until master array is empty.
Then to create the unique-element-array is quite a simple cycle, i wrote a similar function that does something like that but it creates an array with the element and how many times it appears in the array (instances):
function uniqueCnt( ori) { // agroups and counts unique elements of an array, scrubs '' elements
var res = []; // resulting array, ori parameter stands for original array
for( let cntA = 0; cntA < ori.length; cntA++) {
for( cntB = 0; cntB < res.length; cntB += 2) if( ori[cntA] == res[cntB]) { res[cntB + 1]++; break; } // if it matches means it's another instance then increase that element count
if( cntB == res.length && ori[cntA] != '') res.push( ori[cntA], 1); // New element found then push it and start count
}
return res; // returns the agrouped array 0:element 1:instances...
}
If you don't want a count of instances, then you would need an even simpler function, you could try modify this one.

Categories

Resources