How to index array's in Google Apps Script? - javascript

I was working on this project and I wanted to make a function that displays an element of a specific column.
Now with javascript I can call any element of an array with
Array[i]
But for some reason this doesn't seem to work in Google Spreadsheets.
var MyFunction(A) {
return A[1];
}
This function yields me nothing.
EDIt: I solved it.
I used this loop with a sort of double indexing:
for (var i=0; i < 37; i++) {
if (A[0][i] < max && A[0][i] != 0) {
var max = A[0][i];
};
};
A[0][0] is the first element if I select a row vector in spreadsheet.
For example, If I select A2:A5 as A, A[0][0] will give me the value of cell A2!
Thanks for the help!

Try this instead:
var MyFunction(A) {
return A[0];
}
The assumption I making here is you are trying to return the first element in the index. In javascript Array index starts at 0 and not 1.

It doesn't work that way. You have to first allow access to the spreadsheet then tell Apps Script which Spreadsheet you're working on through openById. After that you can now access the data. Here's a simple code snippet for you. I'm using the standalone mode. There maybe other ways of doing this.
We're going to access this sheet. I've included the index numbers so you can easily understand:
Then we create a function named fetchValue which accepts 2 parameters- the row and the column.
We execute main function which makes calls to fetchValue.
function main(){
Logger.log("the value returned was "+ fetchValue(1,1) ); //returns las vegas
}
function fetchValue(row,col) {
var sheet = SpreadsheetApp.openById("SPREADSHEET_ID_HERE");
var data = sheet.getDataRange().getValues();
return data[row][col];
}
We then view the Logs if it returned the value of index 1,1 which is las vegas.
Make sure the row and column you pass to fetchValue is within range else it will return errors.

Related

Simple Counting Function in Google Scripts

Just started learning how to write functions in Google Script Editor. Please help me fix my error. I'm also unable to get the Logger.log(var) function to produce anything in the log.
Goal of the function: count attendance based on whether an 'X' was listed next to a name. Currently the range being iterated through is hard coded. Is it possible to pass through a custom range when the function is called?
Thank you.
function attendance() {
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var count = 0
for (n=6; n<12;++n){
var cell = values[n][2];
if (cell == "X"){
count = count +1;
}
}
return (count);
}
Yes, you can use the length of the values array to make it more dynamic.
n < values.length

Google apps script - Mapping arrays

I am working on a project where I take multiple column/row inventory sheets and turn them into a multi-row/2-column format for order picking.
I have a switch for selecting the appropriate inventory sheet and a map() function that copies the imported information from the inventory DataRange().
However, not all the data is in consistent columns. What I would like to do is find an expression that maps the next column in if the column it was mapping has a zero or "" value.
I won't give you the full body of code unless you need it, but hopefully just the important parts.
This is what I have:
var source = SpreadsheetApp.openById("1xixIOWw2yGd1aX_2HeguZnt8G_UfiFOfG-W6Fk8OSTs"); //This sheet
var srcSht = SpreadsheetApp.getActive();
var sourceMenu = srcSht.getRange('A1');//This is the cell cotaining the dropdown
var menuTest = sourceMenu.getValue();
// Variable for the vars sheet. If it doesn't exist, create it
var varsTest = source.getSheetByName('vars');
if (!varsTest){source.insertSheet('vars');}
var importedA1 = varsTest.getDataRange().getA1Notation();
varsTest.clearContents();
var t1Imp = '=ImportRange("test1_Id", "Stock!A1:F11")';
var varsData = varsTest.getRange('A1');// This is the cell we fill with the importRange formula
varsData.setValue(t1Imp);
var imported = varsTest.getDataRange().getValues();
var newOrder = imported.map(function(item) {
if (item[4] !== NaN){return [[item[0]],[item[4]]];};
if (item[4] === NaN){return [[item[0]],[item[3]]];};}
var orderRange = source.getSheetByName('Sheet1').getRange(10,1,newOrder.length, newOrder[0].length);
orderRange.setValues(newOrder);
Logger.log("\t" + newOrder);
Logger.log(newOrder):
[(timestamp omitted)] items1,order,caramel,6,c&c,2,mint,3,PB,0,,,items2,,caramel,,strawberry,,mint,,PB,
It seems to be skipping the if statements, or I told it that I mean to test the index as NaN, which will obviously never be true.
I also tried replacing 'NaN' with 'undefined'. Same result. I tried finding the item[4].Values, but it gave me an error. I also tried the same logic using filter() instead of map() but it copied the entire data set.
I pull these values onto a new 'vars' sheet in the workbook (to minimize calls to the web service):
test1
reduce them to the first and last columns, then output:
test
The cells in the 'order' column for the second set of items in the 'test' sheet are blank. The values for that order column should be in item[3] of that array, but I can't get the script to identify that that the blank cells are blank.
I am new to Google Apps Script and JS, but I am watching a lot of tuts and learning by doing. If I find a solution, I will post it.
Thank you StackOverflow, I could not have learned as much as I have without this community!
I have a working function that does what I want. In short:
I had to create a duplicate of the order column in a new column, so that all the values would line up. It's not technically a JS answer, but was the simplest and follows good spreadsheet rules.
function rmZeroOrderPS(item){
var source = SpreadsheetApp.openById("<sheetId>"); //This sheet
var varsTest = source.getSheetByName('vars');
var imported = varsTest.getDataRange().getValues();
var i=-1;
while (i <= imported.length){
if(item[8]!= 0) {return [item[0],item[8]]};
i+=1;
};

Concatenate tables in one and insert it in Google Spreesheet

I've got this basic script for Google Sheets, in G.Script
var a = [['1','2']];
for (var i = 0; i <5; i++){
var range = sheet.getRange("A"+i+":B"+i);
range.setValues(a);
}
As you can see, I write 5 times the table "a" in the sheet. What I'd like in concatenate all the tables "a" in one table and write this table, once in the sheet.
I've tried with concat(), but it does not work. So maybe I don't use it properly.
Could you please help me ?
Many thanks
B.
I recommend that you create your spreadsheet range based on the size of your array "a". Rather than creating a cell range such as A1:B5, I recommend using number values to define the range that you would like to update.
Also, updates to Google Sheets run much faster if you update an entire range at one time.
function myFunction() {
var data = [];
data.push([1,2]);
data.push([2,3]);
data.push([3,4]);
if (data.length > 0) {
SpreadsheetApp.getActiveSheet()
.getRange(1,1,data.length,data[0].length)
.setValues(data);
}
}

keeping localStorage objects that iterate whilst using clear() within the same function

I'm trying to clear all local storage when the user either completes the game loop or starts a new game, but also keep some values.
I can do this already with my sound values for volume:
// inside a conditional statement that fires when the user chooses to start a new game.
if (newGameBool === '1') {
var tst = myAu;
//myAu is the stored value that the user sets as sound using a range type input
localStorage.clear();
localStorage.setItem("Au", tst);//A newly cleared localStorage just got a new value, and it's the same as it was before.
UI.myLoad();//reload the function that uses LS to do things.
}
How do I do this for key's that have an iterating number attached to them?
Here is how I save them:
var i = +v + +1;
localStorage.setItem("v", i);
var vv = localStorage.getItem("v");
localStorage.setItem("LdrBrd_" + vv, JSON.stringify(LdrBrd));//saves all data with the iterating key name.
Calling them the way i did the sound function:
var gv = v + 1;//v calls the value from LS and adjusted for off-by-one error. gv is a local variable.
if (newGameBool === '1') {
var ldd, vg;
for (var ii = 0; ii < gv; ii++) {
var ld = localStorage.getItem("LdrBrd_" + ii);
if (ld != null) {
//these are the values that i want to pass beyond the clear point
ldd = JSON.parse(ld);//JSON string of data saved
vg = ii;//how many of them.
}
}
localStorage.clear();
for (var xx = 0; xx < vg; xx++) {
var nld = localStorage.getItem("LdrBrd_" + xx);
if (nld != null) {
localStorage.setItem("LdrBrd_" + ii, JSON.stringify(ldd));
}
}
localStorage.setItem("v", vg);
UI.myLoad();
}
I have been using console.log() in various spots to see what is going on. I comment-out the clear function just to see if the values were wrong and they don't save all all. I tried to make a fiddle, but the local storage wasn't working at all there. In visual studio, it works fine but the script to this file is almost 2000 lines long, so i tried to dress it up the best i knew how.
Thanks in advance for any help or guidance.
I was stuck on this for a few days, but i think i found something that will work, so i'll answer my own question in case there is value in posterity.
locatStorage.clear();
/* ^LS clear() function is above all new setItem codes, some variables are declared globally and some are declared at the top of the functional scope or as param^ */
var itemClass = document.querySelectorAll(".itemClass");//the strings are here
if (itemClass) {//make sure some exist
for (var p = 0; p < itemClass.length; p++) {//count them
mdd = JSON.parse(itemClass[p].innerText);//parse the data for saving
localStorage.setItem("v", v);//this is the LS item that saves the amount of items i have, it's declared at the top of the functions timeline.
localStorage.setItem("LdrBrd_" + p, JSON.stringify(mdd));//this setItem function will repeat and increment with 'p' and assign the right string back to the key name it had before.
}
}
The key is to keep the strings physically attached to an element, then call the class name. The i ran a loop counting them. 'mdd' will spit back each item i want. So then all that is left to do is re-set the item back to it's original status.
This has allowed me to create a way for my users to collect trophies and keep them even after clearing the localStorage when the he/she decides to start a new game.
I use CSS to hide the text from the string.
color:transparent;
In my gameLoop, i have a function that will read the saved strings and show them as cards just below the hidden strings.
Since you want to keep some values I recommend one of two things:
Don't call localStorage.clear() and instead only wipe out the values that you want using localStorage.removeItem('itemName'). Since you said the item names have a numeric component, maybe you can do this in a loop to reduce code.
Pull item(s) that you want saved first and restore them after calling clear(). This option is best if there are way more items that you want removed rather than saved (see below)
function mostlyClear() {
var saveMe = {};
saveMe['value1'] = localStorage.getItem('value1');
saveMe['anotherValue'] = localStorage.getItem('anotherValue');
localStorage.clear();
for(var prop in saveMe) {
if(!saveMe.hasOwnProperty(prop)) continue;
localStorage.setItem(prop, saveMe[prop]);
}
}

Using .getValue() in a loop. This results in many calls. Is there a simpler way to do this?

Sorry if this is a bit newbie.
What I'm trying to do is take a column and delete the values in some of its cells if they do not contain a formula.
function clean() {
var ss = SpreadsheetApp.openById("KEY_REMOVED_FOR_OBVIOUS_REASONS");
var sheet = ss.getSheetByName("TEST");
var limit = sheet.getMaxRows();
for(var i = 6; i < limit; ++i){ // Declares variable "i", which represents the row we are currently checking. Basically says "if our row isn't the last one, execute this block then add to i".
var range = sheet.getRange(i,2);
if (range.getValue() != "") { // Leverages the fact that .getValue() cannot return functions. If our range is blank, execute this block.
range.clear();
}
}
}
I established a loop which checks every row one by one. What I'm wondering is, is there a more efficient way to do this than having to call the server every time I do the loop?
getValues() exists of course, but I'm not sure how one might interact with only the desired cells returned by that.
I did some testing. .getValues() will return the computed values if there are formulas.
The following code works for me.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getLastRow();
var range = sheet.getRange(1,1, rows);
var values = range.getFormulas();
range.clear();
range.setFormulas(values);
}
Short answer
Regarding the too many calls issue, use getLastRow() instead of getMaxRows() and use the returned value (number of rows) to get the required range in just one call.
Explanation
getLastRow() returns the position of the last row that has content while getMaxRows() returns the maximum height of a sheet regardless of content.
getRange(row, column, numRows) returns in just one call the complete range.

Categories