I took this code from a sheet that was provided to me to use, but it's inefficient and times out before completing. I'm trying HARD to re-write it in a super simple format to get it to complete within the time limit. This is what I started with:
function addTo() {
var ss = SpreadsheetApp.getActiveSheet();
// Style 1
// Size 2
var num1 = ss.getRange("AL2").getValue();
var num2 = ss.getRange("O2").getValue();
ss.getRange("O2").setValue(num1+num2);
ss.getRange("AL2").clearContent();
// Size 4
var num1 = ss.getRange("AM2").getValue();
var num2 = ss.getRange("P2").getValue();
ss.getRange("P2").setValue(num1+num2);
ss.getRange("AM2").clearContent();
Then repeat that about 200 times. Someone suggested processing as a range instead. That makes sense and should result in a faster process. But I'm having trouble writing it. This is what I have so far (it's not working though):
function addallatOnce() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var startrange = ss.getRange("Inventory Received!B2:U48");
var addrange = ss.getRange("Inventory Received!Y2:AR48");
var complete = startrange+addrange
ss.getRange("Inventory Received!B50:U96").setValues(complete);
}
I don't usually write code, I can understand what I'm reading, but I don't know anything about writing it. Help would be greatly appreciated. Thank you in advance.
With Google Script
Google has some excellent documentation on optimizing code. In this case you can perform batch operations using arrays. First feed the spreadsheet data into the arrays using the getValues() function on the ranges. Note that getValues() returns a 2D array of the values within the range. Loop through all the values and add them together, value by value. Then assign the desired range the value of the complete array.
function addallatOnce() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var startrange = ss.getRange("Inventory Received!B2:U48").getValues();
var addrange = ss.getRange("Inventory Received!Y2:AR48").getValues();
var complete = addrange;
var row = 0;
var col = 0;
for (row = 0; row < complete.length; row++) {
for (col = 0; col < complete[0].length; col++) {
complete[row][col] = addrange[row][col] + startrange[row][col];
}
}
ss.getRange("Inventory Received!B50:U96").setValues(complete);
}
Without Google Script
Put the following formula in cell B50 of Inventory Received:
=ARRAYFORMULA(B2:U48+Y2:AR48)
See documentation here.
Related
Completely new to using the AppsScript in Google but I came across a script that would allow GoogleSheets to count cells if coloured, and it automatically updates thanks to that last bit of code that sets a random value that essentially triggers a recalculation:
// Unsed third argument
function countColoredCells(countRange,colorRef,unUsed) {
var activeRg = SpreadsheetApp.getActiveRange();
var activeSht = SpreadsheetApp.getActiveSheet();
var activeformula = activeRg.getFormula();
// The regex matches the parentheses as an array, gets the arguments as a string,
// then splits the arguments on the comma into another array
var arrayOfArguments = activeformula.match(/\((.*)\)/).pop().trim().split(',');
// Get the first argument which is the range
var countRangeAddress = arrayOfArguments[0];
// Get the second argument, which is the reference color
var colorRefAddress = arrayOfArguments[1];
var backGrounds = activeSht.getRange(countRangeAddress).getBackgrounds();
var BackGround = activeSht.getRange(colorRefAddress).getBackground();
var countCells = 0;
for (var i = 0; i < backGrounds.length; i++)
for (var k = 0; k < backGrounds[i].length; k++)
if ( backGrounds[i][k] == BackGround )
countCells = countCells + 1;
return countCells;
};
// If Cell A1 has ColouredCells, Writes a random number to B1
function onEdit(e) {
if SpreadsheetApp.getActiveSheet().getRange('A1')="ColouredCells" {
SpreadsheetApp.getActiveSheet().getRange('B1').setValue(Math.random());}
}
Specifically I'm looking to edit the last part of the code so that this script only runs on sheets which I'd like it to (instead of randomly changing my B1 cell on all sheets I touch). As per the comments I'd like it to only run if cell A1 has the string "ColouredCells". Sorry newbie to all this so apologies if this is a really simple ask! Appreciate any help I can get on this one, I've tried googling quite a few different things but can't seem to to find the solution on this one!
This is the part I specifically need help with.
// If Cell A1 has ColouredCells, Writes a random number to B1
function onEdit(e) {
if SpreadsheetApp.getActiveSheet().getRange('A1')="ColouredCells" {
SpreadsheetApp.getActiveSheet().getRange('B1').setValue(Math.random());}
}
Thank you!
To run the code for specific sheets only you have to first get the name of the sheet and with an if-statement run the code as you wish
function onEdit(e){
// code
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if ("Sheet 1" === sheet.getName() || "Sheet 2" === sheet.getName()) {
// Run your code
}
}
Does anyone have an idea of why my code is throwing the "Service Spreadsheets failed while accessing" error? I've read that this is usually caused by a large dataset, but all the datasets involved here are tiny. For whatever reason, the error is being thrown on line 6. I'm new to both Apps Script and Javascript, but I don't think this code should take very long to run at all. The function simply aims to take values from one sheet and drop them into their corresponding place on another sheet -- I've used formulas in the sheet to find the column number (that is the column_nums var) although ideally I would find an all-script solution. The reason I'm doing this through script and not a simple index match is because I want the values from sheet A to be updated over time and use sheet B to periodically (on a trigger) paste in sheet A's values to track them over time. Apologies if this is a basic question, thanks so much!
function export_maxes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
var new_maxes = source.getRange("E1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
var column_nums = source.getRange("A1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
// find row
var row_num = destination.getRange("B1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getHeight();
var row_num = row_num + 1;
// find columns and input maxes. start at 1 bc data has headers
for (var i = 1; i < new_maxes.length; i++) {
destination.getRange(row_num, column_nums[i]).setValue(new_maxes[i])
}
};
There is ongoing issue tracker for "getDataRegion failed when it faces hidden rows or columns". Alternative solution is to show the hidden column groups by using method:expandAllColumnGroups() and hide the column after you fetched the data by using method:collapseAllColumnGroups()
Your code should look like this.
function export_maxes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
source.expandAllColumnGroups();
var new_maxes = source.getRange("E1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
var column_nums = source.getRange("A1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
// find row
var row_num = destination.getRange("B1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getHeight();
source.collapseAllColumnGroups();
var row_num = row_num + 1;
// find columns and input maxes. start at 1 bc data has headers
for (var i = 1; i < new_maxes.length; i++) {
destination.getRange(row_num, column_nums[i]).setValue(new_maxes[i])
}
};
References:
expandAllColumnGroups
collapseAllColumnGroups
noob here, so the script works great if there are less than 800 rows on a sheet, however this time I have a sheet of almost 1500 rows and the script times out.
Basically it is a quick way to get a quote. (quick here means 5-6mins, not an issue) It hides columns with calculations, hides columns with sensitive information and rows where there was no value in column H.
What I want to know is, if I can do the same with a different code, or if someone knows how to make getRange().getValue(); start at the bottom of the sheet, then I could have two scripts starting one after the other to finish the sheet and produce a printable quote.
Any help is greatly appreciated.
Many Thanks
here is the script:
function Quote()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Quote"); `
var datarange = s.hideColumns(6);
var datarange = s.hideColumns(9);
var datarange = s.hideColumns(10);
var datarange = s.hideColumns(12);
var datarange = s.hideColumns(13);
var datarange = s.hideColumns(14);
var lastRow = s.getLastRow();
for( i=1 ; i<=lastRow ; i++) {
var status = s.getRange("H"+i).getValue();
if (status == "") {
s.hideRows(i);
}
}
}
Your problem is row:
s.getRange("H"+i).getValue()
This code takes data from spreadsheet, this is very slow process when you use it inside loop. You may use this conctruction:
var data = s.getDataRange().getValues();
for (var i=0; i < data.length; i++) {
var status = data[i][7]; // takes column H
// other code goes here...
}
This way you read data from the spreadsheet only once using getDataRange(), and convert it into array with getValues(). Then loop should work way more faster.
When hiding rows, remember to add 1 because array starts from 0, heres code for hiding rows inside loop:
if (status == "") {
s.hideRows(i + 1); // adding 1
}
I want to create a function that will iterate over every sheet until a given sheet. The function receives the name of that last sheet as an argument.
function getUntilMonthSavings(month) {
var spreadsheet = SpreadsheetApp.getActive();
var monthSheet = spreadsheet.getSheetByName(month);
var allSheets = spreadsheet.getSheets();
var sheetNumber = monthSheet.getIndex();
var totalSavings=0;
for (var i = 1; i < monthSheet; i++){
totalSavings = totalSavings + allSheets[i].getRange("I20").getValue();
}
return totalSavings;
}
My problem is that what is returned is always 0. I've also returned i to check if it is being iterated, but it returns 1 even when the sheet index is greater than 1.
I'm sure to be doing some kind of basic blunder, but I'm quite at a loss as why this code is not working.
MonthSheet is an object and not something you can compare i to in your loop. You need the actual index of the sheet referred to.
Try:
for (var i = 0; i < monthSheet.getIndex(); i += 1) {
I am using Google app scrips and I want to iterate through a spreadsheet that will be updated weekly. (this is why i dont want to set a range i want to be able to iterate through the entire sheet.)
Is this possible? If yes can you give an example of how this would be done?
and if this isn't a good idea, why not?
Thank you!
Example code
function doGet(e){
var ss = SpreadsheetApp.openById('key in here');
var sheet = ss.getSheetByName("Form Responses");
var range = sheet.getRange(1,1);
var dataRange = range.getValue().toString();
app.add(app.createHTML("There are " + dataRange + " posts today"))
return app;
Something like this, but I want to be able to see the whole sheet not just the range
Henrique Abreu and Srik provided you with correct answer - getDataRange on sheet should serve your purpose.
function doGet(e){
var sheet = SpreadsheetApp.openById('spreadsheetId').getSheetByName('sheetName');
var data = sheet.getDataRange().getValues();;
...
}
data is now 2-dimensional array, you can iterate through it, get last column/row containing content etc. Please read through class reference here: https://developers.google.com/apps-script/reference/spreadsheet/sheet
You cannot iterate over the sheet by itself, but always iterate over the vales of range.
However you don't have to use a fixed range but can compute the range at every script execution.
To do this you could use getlastcolumn() and getLastRow().
This two methods return the last column or row with content in it.
A commodity method is getDataRange() which directly gives you the range with data in it.
Every script execution this range will extend over all your cells and columns with content.
Look at the example from Google API documentation:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// This represents ALL the data
var range = sheet.getDataRange();
var values = range.getValues();
// This logs the spreadsheet in CSV format with a trailing comma
for (var i = 0; i < values.length; i++) {
var row = "";
for (var j = 0; j < values[i].length; j++) {
if (values[i][j]) {
row = row + values[i][j];
}
row = row + ",";
}
Logger.log(row);
}
Hope that helps.