Google Apps Script .add Item recursively? - javascript

So after taking a look at this URL: Adding Items
The basis is that I can't really add items recursively to a new menu. I seem to have made some progress with the following code:
function onOpen(e) {
var menu = SpreadsheetApp.getUi().createAddonMenu();
var vendorsheet = SpreadsheetApp.openById('1Bt4s9aOfjkCyZRvHZMjMdntgID2VYF7Qzmjc7Z7YP1E')
for(var i = 2; i < 5; i++){
var j = 4;
menu.addItem(String(vendorsheet.getRange('A'+ String(i)).getValue()),'Test')
if(i = j){
menu.addItem(String(vendorsheet.getRange('A'+ String(i)).getValue()),'Test')
} else {
menu.addItem(String(vendorsheet.getRange('A'+ String(i)).getValue()),'Test')
}
}
menu.addToUi();
}
It only adds two items. It seems as though the menu.addItem function only adds one item per one call of it. You can't use a for loop to call it recursively. If someone could help me add "n" items recursively for any n that would be preferable. Thank you.

First of all, your code is not recursive. As corn3lius rightly says, this code is iterative. I'm pretty sure I know why your code is malfunctioning though.
Your if statement is actually an assignment statement. You want if (i == j) not if (i = j)
Edit:
To clarify, why you're only getting two entries:
1) i = 2 // You assign 2 to i
2) addItem(...) // Entry one
3) if (i = j) // This is actually considered a trueish statement and gives you your boolean true and sets i = 4
4) addItem(...) // Second entry
5) i++ // i is now equal to 5
6) if (i < 5) // False, 5 == 5 not 5 < 5, exit the loop.

Related

Why doesn't console.log() concatenate strings?

In this example I try to print one asterisk on the first line, two on the second and so on:
var n = 5;
for (i = 0; i < n; i++) {
for (j = 0; j < i; j++) {
console.log('*');
}
console.log('\r\n');
}
The output should look like this:
*
**
***
****
*****
But in the Chrome console I see this:
*
2 *
3 *
4 *
5 *
Why is this?
console.log is not designed to precisely control the rendering of the output. It is a log writer, not a terminal layout API.
It puts each log message on a line of its own, and collapses duplicate, sequential log messages into a single message with a count of the number of times it occurred next to it.
It's doing that because it's how dev tools handles multiple occurrences of the same value for the purposes of clarity.
You can use an array to achieve the effect you're after. Also note that we change j<i to j<= so that it outputs on the 5th.
Then, we use the .join('') method to flatten the array into a string.
Also, since we are console.logging on every iteration loop, a new line will be added automatically as it's one log per loop.
var n = 5;
for (i = 0; i < n; i++) {
var arr = [];
for (j = 0; j <= i; j++) {
arr.push('*');
}
console.log(arr.join(''));
}
Please note that you wouldn't need an array if you were printing this into the DOM - it would work as you originally intended it to do.
Update: Ivar made a great solution as well and that's to use the String.repeat() function which only requires you to use one loop.
var n = 5;
for (i = 1; i <= n; i++) {
var str = '*';
str = str.repeat(i);
console.log(str);
}
Because that's the way it works, basically. Each call to console.log outputs a new log entry. Each log entry is in its own line.
If you log the same string multiple times, they will get summarized into one entry with the count in front.
Don't mistake console.log with Console.Write in C# or similar. It's more like Console.WriteLine.

Google apps script - Broken for loop

I'm working in Google apps script and seem to have screwed up one of my for loops. I'm sure that I am missing something trivial here, but I can't seem to spot it.
Code Snippet:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var lastRow = sheets[3].getLastRow();
var zw = sheets[3].getRange(2, 1, lastRow - 1, 26).getValues();
for (var j = 0; j < zw.length; ++j) {
if (zw[j][9] === 'Yes') {
var masterEmail = [];
var firstLetterLastName = [];
var first2Letter = [];
var masterEmail.push(zw[j][22]);
var firstLetterLastName.push(zw[j][1].charAt(0).toLowerCase());
var first2Letter.push(zw[j][1].charAt(0).toLowerCase() + zw[j][1].charAt(1).toLowerCase());
//The rest of the function follows...
}
}
What's Not Working:
The for loop doesn't increment. When running the code in a debugger, var j stays at a value of 0.0, and the rest of the function only runs based of off the values in the 0 position of zw.
What I need it to do (AKA - How I thought I had written it:)
The ZW variable is holding a 2 dimensional array of cell values from a Google sheet. I'm looping through that, checking the 9th value of each array entry for a string of "Yes" and then running the rest of the function (for each column with a "Yes") if the condition is true.
I thought I had this working before, but recently had to restructure and optimize some things. Now I'm starting to think I may need to rethink things and use a different loop method. Can anyone educate me?
Edit: Here's a bit more context as requested:
function menuItem1() {
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Are you sure you want to send emails?', ui.ButtonSet.YES_NO);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var lastRow = sheets[3].getLastRow();
var zw = sheets[3].getRange(2, 1, lastRow - 1, 26).getValues();
if (response === ui.Button.YES) {
for (var j = 0; j < zw.length; j++) {
if (zw[j][9] === 'Yes') {
var firstLetterLastName = [];
firstLetterLastName.push(zw[j][1].charAt(0).toLowerCase());
//Other Stuff....
}
}
}
}
I have a menu item attached to a simple onOpen, that calls menuItem1(). Calling the function prompts the user with a warning that they are about to send emails, then goes about getting data to assign email addresses based on the contents of the sheets. firstLetterLastName is an example.
I'm still not getting the loop to function, is it because I have it between two if statements? (Here is a link to the sheet)
Indeed it is quite trivial. You have mixed up your increment. You wrote
for (var j = 0; j < zw.length; ++j)
which means that you do 1 + i (and we know that at the start i = 0 which means your value will always be 1) instead of using the usual
for (var j = 0; j < zw.length; j++)
which would mean that you do i + 1 and update i, so you will get the expected 0 + 1 1 + 1 etc
EDIT:
First, I recommend instead of something like
if (responseMir === ui.Button.YES) {
// Your For loop
doing
if (responseMir !== ui.Button.YES) {
return
}
and in a similar fashion in the for loop
if (zw[j][9] !== 'Yes') {
break
}
It mostly helps increase readability by not including large blocks of code under a single if, when all you want to do is to stop execution.
Your for loop gets broken because of the mistake here:
teacherEmailMir.push(selValsMir[j][7]);
So your loop will go over once. However on the next itteration, you try to push selValsMir[1][7] which does not exist. Note that each itteration you have var selValsMir = []; inside the loop, which means that for every j selValsMir will always be an empty array. So with the following line
selValsMir.push([zw[j][0], zw[j][1], zw[j][2], zw[j][3], zw[j][4], zw[j][5], zw[j][7], zw[j][22], zw[j][23], zw[j][24]]);
your array will always have selValsMir.lenght = 1 and selValsMir[0].length = 10. So obviously trying to access anything from selValsMir[1] will throw you an error and stop the script right there.
I also recommend looking over the if statements that look at the first and first 2 letters of the name as I believe you can accomplish the same with less code. Always try to streamline. Consider using switch() where you end up using a lot of else if

How to create and display result one at a time in an infinite loop in javascript?

I'm fairly new to HTML and Javascript. I want to know how to create an infinite loop from, let's say, myArray, list, or whatever and then display result one at a time. Can you please give me an example, hints, or anything with detailed explanation of how it works? I just want to understand on how things work. Thanks!
A very basic loop is a while loop:
while (condition) {
//code block to be executed
}
Typically you would use it like so:
var i = 0;
while (i < 10) {
//code block to be executed
i++;
//This block of code will continue until i >= 10
//adding 1 to the value of I each iteration
}
Easiest way to do a endless loop:
while (true) {
code block to be executed
}
//true will always be true so this will continue until it
//hits a return; statement, the end of time, or the software
//or hardware gives up
A common mistake that end up in an endless loop:
var i = 0;
while (i < 10) {
code block to be executed
//In this example i is never being increased so
//i will always be less than 10
}
A very practical way to do a while loop correctly:
var array = ['a','b','c'];
var i = 0;
while (i < array.length) {
alert(array[i]);
i++;
}
//This will alert a, alert b, then alert c
Another way to do the above is using a for loop:
var array = ['a','b','c'];
for (var i = 0; i < array.length; i++) {
alert(array[i];
}
//for loops are a good practice because you are less
//likely to leave out steps like defining the iterator,
//or increasing the iterator
OP
I'm trying to create something using HTML/Javascript that everytime I press a button called next item (I created one using <form></form>) it would display an item, or an image. The trick is I don't know how to keep displaying the item after the last position. After the last position it should go back to the first position. For example, for the array that you provide in your example you have [a, b, c], after I displayed c and press the button again I want to display a again and so forth. Can you give me hints or any other valuable info on how to do this?
Answer
JSFiddle Example:
http://jsfiddle.net/d2809p6z/
HTML
<button id="button">click me</div>
JS
var array = ['a','b','c'];
var index = 0;
document.getElementById('button').onclick=function(){
if (index >= array.length) {
index = 0;
}
alert(array[index]);
index++;
};

Infinite recursion in JavaScript quicksort?

Here is the quicksort code I wrote. The function doesn't work because it can't reach the base case. If I log the pivot, r and l to the console, they remain the same no matter how many times the sort function is called. So I wonder if the argument l, r are not really passed into the function as data. Why did it happen?
function sort(data){
if(data.length < 2){
return data;
}
else{
var l = [];
var r = [];
var pivot = parseInt(data.length/2);
for(i=0; i<data.length; i++){
if(data[i] > data[pivot]){
r.push(data[i]);
}
else{
l.push(data[i]);
}
}
return sort(l).concat(sort(r));
}
}
I think that the issue here is that your partitioning step does not necessarily shrink the input array. For example, let's trace what happens if you try sorting [1, 2]. In this case, your pivot element will be the element 2. Since 1 > 2 is false, 1 is added to the list l. Since 2 > 2 is false, 2 is added to the list l. As a result, your recursive call on the list l will have exactly the same arguments as your original call, causing infinite recursion.
To fix this, try splitting the input into three lists - one of smaller values, one of equal values, and one of greater values. This code is shown here:
function sort(data){
if (data.length < 2){
return data;
} else {
var l = [];
var r = [];
var e = [];
var i = 0;
var pivot = (data.length / 2) | 0;
for(i = 0; i < data.length; i++) {
if (data[i] > data[pivot]) {
r.push(data[i]);
} else if (data[i] < data[pivot]) {
l.push(data[i]);
} else {
e.push(data[i]);
}
}
return sort(l).concat(e, sort(r));
}
}
This new version explicitly groups the equal elements into their own list, so they aren't recursively sorted by either of the recursive calls. It also gracefully handles duplicate elements.
If you pick the largest value of the array as the pivot element, then all values of data will end up in the array l and none in r. Thus will make the recursion never stop (and keep l, r and pivot at the same values).
Unless this is a brain excercise, using data.sort() should do a better job. ;)
JavaScript passes objects by reference (arrays are objects too). If you want to pass them by value, you need to use the splice function as explained here.
Note that this will create a lot of copies of your data. You probably want to use the native sort() function.

Break loop based on element not existing

I have built a cms that allows users to add up to 10 images into the slideshow, which all output in the front end in divs with ids of showcaseSlide with a number from 0-9 appended on the end, e.g. showcaseSlide0, showcaseSlide1 etc. For the javascript that controls the slideshow, I need to output all of the div id's into an array, but end the array when the slides finish, eg if the div ids went from showcaseSlide0 - showcaseSlide3, I would need the array to go from slides[0] - slides[3].
Here is the current code and some commented out code that I have tried before:
var slides = new Array();
var count = 0;
for(i=0; i<=10; i++){
slides[i] = "showcaseSlide"+i;
document.write(slides[i]); //so that I can see which id's are in the array
var div = document.getElementById(slides[i]);
//if(div) { break; } <- doesn't break
//if(document.getElementById(slides[i]) == null) break; <-breaks after 1st
//if(document.getElementById(slides[i]) == undefined) break; <- breaks after 1st
};
Edit:
I've found out (thanks to Teemu who commented below) that it wasn't working because it was called before the page load, therefore before the objects were rendered. I also have to thank Peter Kelly (who also commented below), who pointed out that I needed to use a ! in my breaking if statement and Frazer who pointed out my loop was 1 too big.
Here is the new code (including the other elements of the initialising function):
var count = 0;
var wait = 4000;
var slides = [];
function startShowcase() {
for(var i=0; i<10; i++){
slides[i] = "showcaseSlide"+i;;
if(!document.getElementById(slides[i])) { break; }
};
setInterval(showcase, wait);
};
I wouldn't be so complex. I guess you have a class applied to all your slides div? If you do, use something like the following:
var slides = []
var divs = document.getElementsByClassName('slide-class')
for (var i = 0, l = divs.length; i < l; ++i) {
slides.push("showcaseSlide" + i)
}
Btw, several comments about your code:
Don't use new Array(). Instead, use []. See here to understand why.
You didn't use the var keyword to declare your i variable, which means this variable is global. Global is evil.
document.write is evil.
I guess your count variable has some use later?
You have DIVs numbered 0-9 but your loop runs 11 times.
Not actual code, but this explains it.
for(i=0; i<=10; i++){
0 = 1st
1 = 2nd
2 = 3rd
3 = 4th
4 = 5th
5 = 6th
6 = 7th
7 = 8th
8 = 9th
9 = 10th
10 = 11th
}

Categories