How to use Google Apps Script to bring in Spreadsheet values efficiently - google-apps-script

I currently use the following code to bring in many values from a spreadsheet
myValues["TitleText"] = ss.getRange('B9').getValue();
I then display the values in a index.html form using the same Google Apps Script like
<div id="title"><?= data.TitleText ?></div>
I was wondering if I should use something else. Maybe like an ArrayFormula?
I guess it looks to me like I am taking many trips back to the sheet to get all of the values that I need. It would be nice to get all values in one visit thus speeding up the loading and processing of the form.
If a one trip approach is possible; what would it look like?

If the data is in an array, there is no "built-in" way to associate one value with another value. With an object, you can associate the "key" with the "value". There are ways to do the same thing with arrays, but it's a lot "trickier". Whether you should use an array or an object, depends on the "bigger picture". If you want to associate a title with a value, and the position of the title and value could be changing in the spreadsheet, then it MIGHT make sense to compile the data in an object before sending it to your HTML. But then there is the issue of performance. It can be better to construct the HTML in the server code, and then send the HTML string back to the client, and not just the data.
If you have lots of scriptlets, you might try having just one printing scriptlet, create the HTML string in the ".gs" server code, and then send the HTML back instead of sending just the data back.
If you compile the data in some special format, send the data, then you need to unscramble the data and construct the HTML, that might be more processing than just building the HTML from the start in the server code.
The getRange() method has four different parameter configurations. You are using the a1Notation variation. I would use this variation:
getRange(starting row, starting column, number of Rows, number of Columns)
Then you can use code like this:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('the sheet name');
var allData = sheet
.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn())
.getValues();
If you only want to get the values in column B, starting in row 2, you can do this:
var colB_Data = sheet
.getRange(2, 2, sheet.getLastRow())
.getValues();
The getValues() method returns a two dimensional array. But if you are only getting one column of data, all the inner arrays only have one element. If you need a one dimensional array, you can (in this case) convert the two dimensional array to a one dimensional array with:
colB_Data = colB_Data.toString().split(",");

Related

Range.SetValues() does not insert data on one sheet, on the other works. What is the reason?

I have a GoogleSheet with basically two sheets, which are very similar in terms of data collected.
I need to calculate same values for both sheets, but source data is in different columns.
Therefore I created three files in AppsScript:
Common.gs - with common function definitions
sheet1.gs
sheet2.gs - both sheet1 and sheet2 have only definitions of proper ranges in particular columns and one function to run script, which essentially calls functions defined in Common.gs, like so in sheet1.gs:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1")
var createdColumn = sheet.getRange("E2:E").getValues()
var ackColumn = sheet.getRange("G2:G").getValues()
var resColumn = sheet.getRange("I2:I").getValues()
var timeToAckColumn = sheet.getRange(2,14,ackColumn.length,1)
var timeToResColumn = sheet.getRange(2,15,resColumn.length,1)
var yearAndWeekRange = sheet.getRange(2,16,createdColumn.length,2)
function calculateMetricsSheet1() {
calculateTimeDiff(createdColumn, ackColumn, timeToAckColumn)
calculateTimeDiff(ackColumn, resColumn, timeToResColumn)
calculateWeek(createdColumn, yearAndWeekRange)
}
example function implementation (they are basically very similar with minor differences):
function calculateWeek(createdColumn, yearAndWeekRange) {
var arrData = []
for(var i=0;i<createdColumn.length;i++) {
if(createdColumn[i][0].toString()=="") {
arrData.push(["",""])
continue
}
var createdDate = new Date(createdColumn[i][0])
var year = createdDate.getFullYear()
var week = Utilities.formatDate(createdDate, "GMT+1", "w")
arrData.push([year, week])
}
yearAndWeekRange.setValues(arrData)
}
the sheet2.gs is basically different column definitions, the functions called within calculateMetricsSheet2() are the same.
So what is the problem?
The script works perfectly fine for sheet2.gs, but for sheet1.gs it does collect proper data, calculates proper data, but the data does not appear in proper columns after Range.setValues() call.
No exceptions or errors appear in the console.
Documentation does not provide any kind of information what could be the problem.
I have really ran out of ideas what could be the cause of the issue.
Does anyone have any idea what is going on?
edit: It may be useful to put emphasis on the fact that each script runs function calling 3 other functions -> all of them end with Range.setValues({values}). And for one sheet all of them work, and for the other - none.
That's the reason I assume there is something wrong with the sheet itself, maybe some permissions/protection? But I couldn't find anything :(
edit2: I modified my code to iterate through the sheet 10 rows at a time, because I thought maybe when I get a whole column, something bad happens with data and breaks setValues() function.
Unfortunately - even if my code iterated 1 row at a time, it still did not work on sheet1, but worked on sheet2. So not a data problem.
The code you show always puts values in yearAndWeekRange which is always in the 'sheet1' sheet. To put the data into another sheet, you need to change the target range appropriately.
The dimensions of the range must match the dimensions of the array you put there. Use this pattern:
yearAndWeekRange.offset(0, 0, arrData.length, arrData[0].length).setValues(arrData);
I found out what is the problem.
Two scripts were pretty identical, even with naming of variables - ie ackColumn, resColumn etc.
Those were stored as a global variables, so even if I was running script1.gs, it used global variables from script2.gs, effectively writing proper data to wrong sheet.
separating global variables names fixed the issue.
Perhaps a rookie mistake, but I missed the fact, that if I have a variable defined outside any function, it becomes global and could be overwritten from other file

Reading a matrix of data into a script function from Google Sheets

I am new to Google Sheet script so I am trying to learn by looking at other professional code. I have noticed that most routines that read in arrays use .getDataRange().getValues() which reads in the whole worksheet.
The worksheets I work on have lots of different data info on any one sheet. Is there an easy way to read in a subset of the sheet and not the whole sheet?
As an example: If I want to read in range D6:J45 (note the ranges can change based on user request):
How can I load in only that matrix data into a 2 dimensional array in Script?
If I have to use nested for loops, .length seems to get me # of rows, how can get number of columns in range?
Use getRange(a1Notation) to get the values of the specified range.
getValues() returns a 2D array. The number of columns corresponds to the length of the inner arrays, so you have to retrieve the length of an element of the outer array, as shown below:
var values = SpreadsheetApp.getActiveSheet().getRange("D6:J45").getValues();
var numberOfColumns = values[0].length;

Google script copy data from a sheet to another in efficent way

I have to copy data from one spreadsheet to another.
On the source I have a sheet with columns DataName and DataID
I need to copy DataName column in SSdest sheet('Data'), DataID in SSdest sheet('ID').
I can do it in several ways, but I need to be time effective due to a big load of data. I pick data using Data = Source.getDataRange().getValues()
I can paste in the correct way using a for loop with setvalue() but is time-consuming. Is there a way to paste only one column from getValues data?
as #Casper told you, you can use range.getValues() to get a multidimensional array then use range.setValues(). You only need to set the range to one column to get all the datas wanted as so : sheet.getRange(rowNumber, columnNumber, numberOfRows).getValues().
One other solution would be to select all the datas with getDataRange(), then programatically using javascript create a new array you will then add using setValues(array)

Change order of columns spreahseet

I just want to confirm if there is any inbuilt function is google apps script to reorder the columns in the spreadsheet or do I have to do something like this -
var r = currentSheet.getRange(1, 1, currentSheet.getLastRow() - 1, currentSheet.getLastColumn()).getValues();
for(var i1=0, dLen=r[0].length; i1<dLen; i1++) {
//something to reorder
}
Please help!
it depends. if you just want to present the data in a different order, just use the built-in "=query" cell function, selecting the rows in the order you want.
query has some limitations thou. for example each column must have a consistent type on all rows (date, number, string etc). there arr other built-in functions that do similar things.
if you actually want to change the source range, you need to do it by getting the entire range array values, transforming it (possibly needing to take care of functions too) and writting the entire new array in a single range write.

How do I create a Range class programatically in my google spreadsheet script?

So I have this google script that I want to use to create charts in my spreadsheet. I'm basically programatically creating content (with the use of spreadsheet data) that I then want to plot. The way I used to do it is by filling one of the sheets with all the data and then using that data to plot, but I was hoping to skip that step and feed the javascript arrays directly into my addRange method.
So I've got a script that creates a new chart:
// insert the scenario chart
var scenarioChartBuilder = sheet.newChart();
scenarioChartBuilder.setPosition(5, 6, 5, 5)
.setChartType(Charts.ChartType.AREA)
.addRange(rangeObject);
sheet.insertChart(scenarioChartBuilder.build());
The problem is; how do I make "rangeObject", given that I only have javascript arrays, and don't want to use actual spreadsheet data? Or is there another way of plotting data that isn't actually in a spreadsheet?
Range data is actually just a multidimensional array.
So a rangeObject could just be defined like;
var rangeobject = [[data, data, data],[data, data, data]];
The first array represents the row and the second array the column data.
programmatically you could get the data like;
var dataFirstRowSecondColumn = rangedata[0][1]; //0 indexed array!
So, to add a range is just to pass a multidimensional array (with content data).
But beware ;-) When adding to a chart i would think that you would have to mind that each column would only contain on kind of data to be valid.
In code you could directly use my first example.