How to access the currently iterated array value in a loop? - javascript

Current attempt using an array of objects with properties:
The objective:
I want to automatically fill out emails on behalf of ~30 different people. The form fields are always consistent, but the values I'm filling in will change on an email-to-email basis. I'm using TagUI to do this.
My old code (last code box below) successfully filled out each form by assigning each line in the .csv to a separate array BUT failed to iterate through the values of a specific column within the .csv. Please see the text above the last code box below for further explanation.
Now I'm starting again, this time aiming to create an array of objects (representing each email being sent) with properties (representing each field to be filled within each email).
Here's what I've got so far:
// Using TagUI for browser automation
// https://github.com/kelaberetiv/TagUI
website-to-automate-URL-here.com
// Set up the arrays to be used later
emails = []
// Load in the 'db.csv' file
// Link to .csv: https://docs.google.com/spreadsheets/d/16iF7F-8eh2eE6kDiye0GVlmOCjADQjlVE9W1KH0Y8MM/edit?usp=sharing
csv_file = 'db.csv'
load '+csv_file+' to csv_lines
// Split the string variable "lines" into an array of individual lines
lines = csv_lines.split('\n')
// Split the individual lines up into individual properties
for (i=0; i < lines.length; i++)
{
emails[i].name = properties[1].trim()
emails[i].recipients = properties[2].trim()
properties = lines[i].split(',')
}
EDIT: The below code has been put on the back burner as I attempt to solve this another way. Solutions are still welcome.
I'm having trouble triggering my for loop (the last one in the code below).
My goal for the for loop in question, in plain English, is as follows: Repeat the below code X times, where X is determined by the current iteration of the total_images array.
So if the total_images array looks like this:
[Total Images, 2, 3, 4, 5]
And the parent for loop is on its third iteration, then this for loop should dictate that the following code is executed 4 times.
I'm using TagUI (https://github.com/kelaberetiv/TagUI), so there many be some non-Javascript code here.
https://www.website.com
wait 3s
// Setting up all the arrays that the .csv will load
array_campaign = []
array_subject = []
array_teaser = []
array_recipients = []
array_exclude = []
array_img1src = []
array_img1alt = []
array_img1url = []
array_img2src = []
array_img2alt = []
array_img2url = []
array_img3src = []
array_img3alt = []
array_img3url = []
array_img4src = []
array_img4alt = []
array_img4url = []
total_images = []
// Load in the 'db.csv' file
csv_file = 'db.csv'
load '+csv_file+' to lines
// Chop up the .csv data into individual pieces
// NOTE: Make sure the [#] corresponds to .csv column
// Reminder: Numbers start at 0
array_lines = lines.split('\n')
for (n=0; n<array_lines.length; n++)
{
items = array_lines[n].split(',')
array_campaign[n] = items[1].trim()
array_recipients[n] = items[2].trim()
array_exclude[n] = items[3].trim()
array_subject[n] = items[4].trim()
array_teaser[n] = items[5].trim()
array_img1src[n] = items[6].trim()
array_img1alt[n] = items[7].trim()
array_img1url[n] = items[8].trim()
array_img2src[n] = items[9].trim()
array_img2alt[n] = items[10].trim()
array_img2url[n] = items[11].trim()
array_img3src[n] = items[12].trim()
array_img3alt[n] = items[13].trim()
array_img3url[n] = items[14].trim()
array_img4src[n] = items[15].trim()
array_img4alt[n] = items[16].trim()
array_img4url[n] = items[17].trim()
total_images[n] = items[18].trim()
}
for (i=1; i < array_campaign.length; i++)
{
echo "This is a campaign entry."
wait 2s
}
// This is the problem loop that's being skipped
blocks = total_images[i]
for (image_blocks=0; image_blocks < blocks; image_blocks++)
{
hover vis1_3.png
click visClone.png
}
This is the most coding I've ever done, so if you could point me in the right direction and explain like I'm a beginner it would be much appreciated.

Look like the only reason make your last loop being skipped is that total_images[i] is undefined, which is used for the loop condition. I believe that the value of i at that moment is equal to array_campaign.length from the previous loop, which is actually out of array range.
Here're some example codes:
const arr = [0, 1, 2];
const length = arr.length; // the length is 3, but the last index of this array is 2 (count from 0)
for (i = 0; i < length; i++) {
console.log(i);
}
// output:
// 0
// 1
// 2
console.log(i); // i at this moment is 3, which is = arr.length and made the above loop exit
console.log(arr[i]); // => undefined, because the last index of the above array is 2, so if you reference to an un-existed element of an array, it will return undefined.
"run the following code X times, where X is determined by the value of total_images[i]" - so, if I understand your question correctly, you can use nested loops to do this:
for (i=1; i < array_campaign.length; i++)
{
echo "This is a campaign entry."
wait 2s
// nested loop, the number of iteration is based on the value i of outside loop
for (j=0; j < total_images[i]; j++) {
// do something here
}
}

My old code should have worked. I opened up the .csv file in notepad and noticed there were SEVERAL extra commas interfering with the last column of data, throwing everything for a loop.
Did some searching and apparently this is a common thing. Beware!

I created TagUI but I don't check Stack Overflow for user queries and issues. Try raising issue directly at GitHub next time - https://github.com/kelaberetiv/TagUI/issues
Looks like you found the solution! Yes, if the CSV file contains incorrect number of columns (some rows with more columns than others), it will lead to error when trying to work on it from your automation script. It looks like the extra commas cause extra columns and broke your code.

Related

Apps Script For Loop stuck at 0 in iteration although it is running

I have an array 'vnData' containing 5, 6 rows from where I am trying to extract 3rd column values (based on a criteria) and insert to a new array. Here is my code
for (odr = 0; odr < vnData.length; odr++){
Logger.log(vnData);
tempOdr = vnData[odr][3];
Logger.log(odr);
Logger.log(tempOdr);
Logger.log(vnData[odr][3]);
for(k = 0; k < vnData.length; k++){
if(vnData[k][3] = tempOdr){
odrVal = odrVal + vnData[k][11];
}
}
if(odrVal > 0){
affOdrSet.push(tempOdr);
}
Logger.log(affOdrSet);
}
Logger gives right value of odr in Logger.log(odr); but in Logger.log(vnData[odr][3]); I am always getting a result where value of odr is 0.
So for each iteration I get value from first row. Please help what is wrong in it.
One more thing, if I log Logger.log(vnData[3][3]) in place of Logger.log(vnData[odr][3]) then for first iteration it gives me right value from row 4 but for all subsequent iterations even Logger.log(vnData[3][3]) gives value from first row which is really weird.
The problem is the expression of the first if statement. Instead of
vnData[k][3] = tempOdr
use
vnData[k][3] === tempOdr
The above because = is the assign operator but it's very likely that instead of assigning tempOdr to vnData[k][3] what you want to compare them.

Where/how to set increment on loop and update array only when condition found?

I'm writing a function to iterate through folders on Google Drive and match files (Google Sheets) with a variable string (a date specified on a table cell). When a matching file is found, the containing folder name string is assigned to folderItems[0] and the file URL to folderItems[1]. Once all matching files within a folder have been found, the next folder is iterated through in the same way. These "folderItems" arrays are stored in a parent array "folderItemsContainer" to create a 2 dimensional array which can then be output to a spreadsheet using .setValues().
I'm having trouble figuring out how or where to put the increment variable so that it will increment only when a filename match is made but not stop a loop when a match isn't found.
I've tried various structures including interchanging for and while loops and inserting if statements where seemingly useful. I've looked at a few different answers on Stackoverflow that come close to making sense but none seem to be applicable here. I'm fairly new to programming. I've got different variations of code I've tried, but this is where I'm up to so far:
function GetFolderData() {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet();
var currentYearPeriod = currentSheet.getRange("C1!A4").getValue();
// Logger.log(currentYearPeriod);
//Get folder objects from parent folder
var parentFolderId = "17F0fcBH0jmxsk2sUq723AuIY0E2G_u0m";
var parentFolder = DriveApp.getFolderById(parentFolderId);
//Get folders from specified parent folder
var StaffFolders = parentFolder.getFolders();
//Create container array
var folderItemsContainer = [];
//Create Item Array
var folderItems = [];
var i = 0;
//For every staff folder, regardless of content, do:
while (StaffFolders.hasNext()) {
//Get current folder object
currentFolder = StaffFolders.next();
//Get files in current folder object as FileIterator
FolderFiles = currentFolder.getFiles();
//If folder empty, outer while loop will iterate
if (FolderFiles !== null) {
//Iterate through existing files
while (FolderFiles.hasNext()) {
//Get file object sequentially
file = FolderFiles.next();
//When filename matches currentYearPeriod, store URL next to name in folderItems
for (i = 0; file.getName().indexOf(currentYearPeriod) !== -1; i++) {
folderItems[i] = [];
folderItems[i][0] = currentFolder.getName();
// Logger.log(currentFolder.getName());
folderItems[i][1] = file.getUrl();
folderItemsContainer[i] = folderItems[i];
}
}
}
}
return folderItemsContainer;
}
function InsertFolderData() {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange("B4:Z1000").clearContent();
FolderData = GetFolderData();
Logger.log(FolderData);
sheet
.getRange(4, 2, FolderData.length, FolderData[0].length)
.setValues(FolderData);
Logger.log(FolderData);
/* var str = "";
for (var i = 0; i < FolderData.length; i++) {
str += FolderData[i] + "\r\n";
}
str = str.substr(0);
var ui = SpreadsheetApp.getUi();
ui.alert("DATA IMPORTED: " + "\r\n" + str);
*/
}
With the above code, I'm not entirely sure why but I seem to be getting stuck in an endless loop and the script doesn't finish. What I'm hoping to achieve is the folderItemsContainer array being populated with arrays containing file information (parent folder name[0] and file URL[1]) for files that match the currentYearPeriod variable. I've been refactoring the code and I've learned a lot but unfortunately not how to solve the problem.
You should check what's the deference between each loop, you are not fully undestending them. If you want to execute the instructions inside the for loop until a certain condition is met, in this case file.getName().indexOf(currentYearPeriod) !== -1, you should use a while loop. The bug is that the previous condition is never met because file never change while running the for loop. Thats why you are having an infinite loop. My solution:
// new variable
var cnt = 0;
while (StaffFolders.hasNext()) {
currentFolder = StaffFolders.next();
FolderFiles = currentFolder.getFiles();
if (FolderFiles !== null) {
while (FolderFiles.hasNext()) {
file = FolderFiles.next();
// You for loop started here
folderItems[cnt] = [];
folderItems[cnt][0] = currentFolder.getName();
folderItems[cnt][1] = file.getUrl();
folderItemsContainer[cnt] = folderItems[cnt];
// each time you read a new file you increment by 1
cnt++;
}
}
// this reset the counter for each new folder
cnt = 0;
}
Deferences between loops:
for loops
They are used when you know how many iteration will be needed. For example, if you want to print all the character of a string in the console:
const str = "hello";
for(let i = 0; i < str.length; i++) {
console.log(str.charAt(i));
}
let i = 0 is the starting point
i < str.length is when you want to stop. If you have to use a simbol which is not one of the follow <, <=, >, >=, you shouldn't be using a for loop.
i++ how you want to reach the stop property.
while loops
If you dont know when your loop is going to end, if it's going to have, 5 iteration, 100 iteration or 0 iteration. You should use while loops.
function getFirstL(str)
let i = 0;
while(i < str.length && str.charAt(i) !== "l"){
i++;
}
}
Your for loop. Here is syntax of for loop.
for (statement 1; statement 2; statement 3) {
// code block to be executed
}
Statement 1 is executed (one time) before the execution of the code block.
Statement 2 defines the condition for executing the code block.
Statement 3 is executed (every time) after the code block has been executed.
Your for loop doesn't define a condition for it to exit. A minimum or maximum value. something like
i<file.getName().indexOf(currentYearPeriod);
So it will check from 0-to that value.

For Loop Behaviour

This question is regarding Javascript for loops, where I've been having some weird behaviour.
My For loop currently looks like this:
var UnitAmount = 2;//(value verified, yet not declared like this)
var DamageAmount = [1,3];//(value verified, yet not declared like this)
function forcerefresh () {
for( i=0 ; i < UnitAmount ; i++ ) {
//(10 lines of Stuff)
var check = 0;
for (c = 0; c < DamageAmount[i] ; c++ ) {
debug[0] = damage[i][c].getElementsByClassName("writedamage")[0];
debug[1] = damage[i][c];
debug[2] = unit[i];
check = check + 1;
console.info("Unit:",i,"Loop:",c,"Debug:",debug,"Check:",check, "Race:",unitrace[i], unit[i].dataset.race);
alert("Debug:"+ debug );
damageCalc(damage[i][c].getElementsByClassName("writedamage")[0],damage[i][c],unit[i]);
}
}
}
In here, debug, alert, Check and the console write call are already added to attempt to find the problem - I'm getting an infinite loop here.
The output im getting in the console shows that while the i constant walks as intended, but the c Iteration count starts at 0, jumps to 2, and then stays 2 - and since the DamageAmount[1] = 3, this creates an infinite loop. And where the c might stick at value 2, the check values does walk up through the iterations.
As for the amount of variables involved, I've checked time and again with the console, and all of them are defined, and the values I expected. The Code is located at http://age-of-wonders-3.wikia.com/wiki/MediaWiki:Units.js, but if any parts are requested I'll of course post them here.
Any help is appreciated!
Try using for of or .forEach on an array

How to compare Array value to result of 'for' loop in javascript

I have an empty array (called zoomthumbsarray) which gets values pushed to it whilst a 'for' loop is running. This 'for' loop is checking if a thumbnail image is present in the backend against the particular product the user is viewing. If there is an image it gets added into a vertical slider. The current issue is there are non colour specific images (like lifestyle shots) that are being added into the slider multiple times.
So I need to check if the image found in the for loop is currently stored in the array. If it is present, the image has already been generated and I don't want it to get pulled into the slider again. If it hasn't then the image will get added.
Below is the code I am working on. I would presume indexOf would be used but can't get this to work.
Any help would be really appreciated.
var zoomthumbsarray = [] // Empty array which gets populated by .push below during loop
for (var i = 0; i < storeImgsArr.length; i++) { // storeImgsArr finds the quantity of attributes present against the product. This loops and increments counter if there is another attibute image
for (var e = 0; e < storeImgsArr[i].images.imgL.length; e++) { // Loop and increment counter if there is a Large image
zoomthumbsarray.push(storeImgsArr[i].images.imgS[e].slice(-16)); // Slices off last 16 characters of image path i.e. _navy_xsmall.jpg or 46983_xsalt1.jpg and pushes this into 'zoomthumbsarray' array
// if statement sits here to build the html to add the image to the slider
}
}
zoomthumbsarray = [] // Resets array to zero
ANSWER
As answered by Chris I used $.unique to only keep unique values in the array.
Then wrap an if statement around the code to build the thumb image html if the array === 0 or if the current image isn't already in the array.
Updated code below:
var zoomthumbsarray = [] // Empty array which gets populated by .push below during loop
for (var i = 0; i < storeImgsArr.length; i++) { // storeImgsArr finds the quantity of attributes present against the product. This loops and increments counter if there is another attibute image
if (zoomthumbsarray === 0 || zoomthumbsarray.indexOf(storeImgsArr[i].images.imgS[e].slice(-16)) < 0) { // If statement is true if array === 0 or if the current image isn't already in the array
for (var e = 0; e < storeImgsArr[i].images.imgL.length; e++) { // Loop and increment counter if there is a Large image
zoomthumbsarray.push(storeImgsArr[i].images.imgS[e].slice(-16)); // Slices off last 16 characters of image path i.e. _navy_xsmall.jpg or 46983_xsalt1.jpg and pushes this into 'zoomthumbsarray' array
zoomthumbsarray = $.unique(zoomthumbsarray); //Keeps only unique elements
// if statement sits here to build the html to add the image to the slider
}
}
}
zoomthumbsarray = [] // Resets array to zero
Some cheap and dirty ideas:
Using underscore/lodash:
zoomthumbsarray = _.uniq(zoomthumbsarray); //Keeps only unique elements
jQuery has one as well:
zoomthumbsarray = $.unique(zoomthumbsarray); //Keeps only unique elements
then you loop through the array and build HTML.
Update:
There's something a bit odd about the rest of the JS. Might this work (if you're using a new enough browser)?
var zoomthumbsarray = [];
storeImgsArr
.map(function(item) { return item.images.imgS; })
.forEach(function(imgS) {
zoomthumbsarray = zoomthumbsarray.concat(imgS.map(function(imagePath) {
return imagePath.slice(-16);
}));
});
zoomthumbsarray = $.unique(zoomthumbsarray);
I have tried indexOf (see first if statement below) but this doesn't work.
As #elclanrs said, indexOf does return the index in the array not a boolean. You only will need to see if it's >= 0 to test whether an image is already contained in the array.
var zoomthumbsarray = [];
for (var i = 0; i < storeImgsArr.length; i++) {
for (var e = 0; e < storeImgsArr[i].images.imgL.length; e++) {
var image = storeImgsArr[i].images.imgS[e].slice(-16);
if (zoomthumbsarray.indexOf(image) < 0) { // not yet in the array
zoomthumbsarray.push();
// and build the html to add the image to the slider
}
}
}
If you have really lots of images and notice this starts slowing the page down, then there are too many images in your page anyway. No, joke aside; …then check the optimisation by #Ivey.
instead of using an array you can use an object to store the images as keys and a dummy value (possibly true). then you can extract the keys from this object.
var images = {};
for (var i = 0; i < storeImgsArr.length; i++) {
for (var e = 0; e < storeImgsArr[i].images.imgL.length; e++) {
images[storeImgsArr[i].images.imgS[e].slice(-16))] = true;
}
}
var zoomthumbsarray = [];
for(var k in images) {
zoomthumbsarray.push(k);
// build the html to add the image to the slider
}
EDIT: Added build html comment

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