setFormulas and rangeList - arrays

I'm trying to setFormulas over a range of non-contiguous cells. I need a formula (they're all different) set every 30 cells in a single column (c).
It works to setFormula for each cell, but creating 56 variables seems unnecessary. I can get the formulas but not set them as intended. I also tried using getRangeList but I'm not sure that does what I think it does. Any advice?
function test() {
var spreadsheetU09U10 = SpreadsheetApp.openById('some url');
var sheetU09 = spreadsheetU09U10.getSheetByName('TEST');
var sheetU10 = spreadsheetU09U10.getSheetByName('U10');
var sheetDATA = spreadsheetU09U10.getSheetByName('Sheet4');
//U09 SHEET
//var rangeListU09 = sheetU09.getRangeList(['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494',
//'C524','C554','C584','C604','C634','C664','C694','C724','C754','C784']);
//Logger.log(rangeListU09);
var startRow = 4;
var startColumn = 3;
var numRows = sheetU09.getLastRow();
var numColumns = 1;
var range = sheetU09.getRange(startRow, startColumn, numRows, numColumns);
var getFormulasU09 = sheetDATA.getRange('C30:C57').getFormulas();
//Logger.log(getFormulasU09);
Logger.log(getFormulasU09.length);
for (var i = 0; i < getFormulasU09.length; i++) {
var setFormulasU09 = range.setFormulas(getFormulasU09);
Logger.log(setFormulasU09);
startRow = startRow + 29;
}

It isn't clear exactly where the formulas you are using are originating from, but the RangeList class can help reduce the read time, even if you use it just to call getRanges. If the formula is the same in R1C1 format, then you can very effectively use RangeList#setFormulaR1C1.
Assuming you have formulas in one region that must be written verbatim in a disjoint set of cells:
const wb = SpreadsheetApp.getActive();
// Assuming only text formulas, not actual "entered" formulas
const formulas = wb.getSheetByName("formulas").getDataRange()
.getValues()
.map(function (row) { return row[0]; });
const sheet = wb.getSheetByName("some name");
const destinations = [
// Depending on the relationship between destinations, one could programmatically generate these
];
// Efficiently acquire references to multiple disjoint Ranges
const rl = sheet.getRangeList(destinations);
// Assume the i-th formula goes in the i-th range
rl.getRanges().forEach(function (rg, i) {
rg.setFormula(formulas[i]);
});
// The RangeList makes uniformly formatting these disjoint ranges extremely simple
rl.setFontWeight('bold');
...
Reference
- RangeList

You want to put formulas to the individual cells.
You want to put 28 formulas to cells of ['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494', 'C524','C554','C584','C604','C634','C664','C694','C724','C754','C784'] in the sheet of TEST.
If my understanding is correct, how about using values.batchUpdate of Sheets API? The flow of this script is as follows.
Set range list as 1 dimensional array.
Retrieve formulas.
Create request body for sheets.spreadsheets.values.batchUpdate.
In order to use this script, please enable Sheets API at Advanced Google Services and API console. You can see about how to enable Sheets API at here.
Sample script:
function test() {
var spreadsheetId = "### spreadsheetId ###"; // Please set this.
var sheetName = "TEST";
var spreadsheetU09U10 = SpreadsheetApp.openById(spreadsheetId);
var sheetU09 = spreadsheetU09U10.getSheetByName(sheetName);
// var sheetU10 = spreadsheetU09U10.getSheetByName('U10'); // This is not used in this script.
var sheetDATA = spreadsheetU09U10.getSheetByName('Sheet4');
var rangeListU09 = ['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494', 'C524','C554','C584','C604','C634','C664','C694','C724','C754','C784'];
var getFormulasU09 = sheetDATA.getRange('C30:C57').getFormulas();
rangeListU09 = rangeListU09.map(function(e) {return sheetName + "!" + e});
var resource = {
data: rangeListU09.map(function(e, i) {return {range: e, values: [[getFormulasU09[i][0]]]}}),
valueInputOption: "USER_ENTERED",
};
Sheets.Spreadsheets.Values.batchUpdate(resource, spreadsheetId);
}
Note:
From your question, I'm not sure about the detail formulas. If the a1Notation of each formulas is required to be modified, can you provide a sample spreadsheet including the formulas?
Reference:
sheets.spreadsheets.values.batchUpdate
If I misunderstand your question, please tell me. I would like to modify it.

I'm assuming that you want to copy the whole column starting from the cell locations in the array. That wasn't really clear to me.
function test109() {
var ss=SpreadsheetApp.getActive();
var shU09=ss.getSheetByName('35');//formulas get copied into here starting at row 4
var shDATA=ss.getSheetByName('36');//formulas stored in here C30:C57
var fA=shDATA.getRange('C30:C57').getFormulas();
var dA=['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494','C524','C554','C584','C604','C634','C664','C694','C724','C754','C784'];
for(var i=0;i<dA.length;i++){
var rgs=Utilities.formatString('%s:%s',dA[i],shU09.getRange(dA[i]).offset(fA.length-1,0).getA1Notation());//this uses range.offset to calculate the correct range in A1Notation.
shU09.getRange(rgs).setFormulas(fA);
}
}
As it turns out I just noticed that there are 28 locations and 28 formulas. Perhaps that was intentional and you want to copy a different formula in each location then this version would do that.
function test109() {
var ss=SpreadsheetApp.getActive();
var shU09=ss.getSheetByName('35');//formulas get copied into here starting at row 4
var shDATA=ss.getSheetByName('36');//formulas stored in here C30:C57
var fA=shDATA.getRange('C30:C57').getFormulas();
var dA=['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494','C524','C554','C584','C604','C634','C664','C694','C724','C754','C784'];
for(var i=0;i<dA.length;i++){
shU09.getRange(dA[i]).setFormula(fA[i][0]);
}
}
Range Offset

Related

Google Apps Script For Loop Through Array Speed

I have a simple example of a function I am working on. I am trying to loop through a column of unique item IDs. If the item ID is found in the item ID column of another sheet, it pulls adjacent attributes from the data table, and assigns them in the same row. I have a function and it works, however, this is a base example. In reality I need to do this for 1000+ rows, and much larger data sets. It is currently taking 30-60 mins to run. I believe there is a much faster way to do this with arrays and using foreach and getvalues I'm just not sure how to get started. Any help would be greatly appreciated.
function example() {
var list = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("List");
var data = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data")
var listendRow = list.getLastRow();
var dataendRow = data.getLastRow();
var dataid = data.getDataRange().getValue();
for (var i = 2; i <= listendRow; i++) {
for (var j = 2; j <= dataendRow; j++){
var idnum = [list.getRange(i, 2,listendRow).getValue()];
var id = data.getRange(j, 3).getValue();
var name = data.getRange(j, 4).getValue();
var weight = data.getRange(j, 5).getValue();
if (idnum == id){
list.getRange(i, 3).setValue(name);
list.getRange(i, 4).setValue(weight);
}
}
}
}
Here is the link to the sheet:
https://docs.google.com/spreadsheets/d/1PPZKRXhiAAfFG1d-CU02MV_CSrqbdsCsyo_QADz5yiA/edit?usp=sharing
I believe your goal is as follows.
Your script works fine. Under this condition, you want to reduce the process cost of your script.
Modification points:
When I saw your sample Spreadsheet, V8 runtime is not used. Please enable V8 runtime. When V8 runtime is used, the process cost of the script can be reduced.
In your script, getValue and setValue are used in a loop. In this case, the process cost becomes high. Ref
SpreadsheetApp.getActiveSpreadsheet() can be declared one time.
In order to reduce the process cost of your script, how about the following modification?
Modified script:
Before you run this script, please enable V8 runtime.
function example2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var list = ss.getSheetByName("List");
var data = ss.getSheetByName("Data");
var obj = data.getRange("C2:E" + data.getLastRow()).getValues().reduce((o, [a, ...b]) => (o[a] = b, o), {});
var range = list.getRange("B2:B" + list.getLastRow());
var values = range.getValues().map(([b]) => obj[b] || [null, null]);
range.offset(0, 1, values.length, 2).setValues(values);
}
When this script is run, the values are retrieved from "Data" sheet and create an object for searching the ID. And, the values are retrieved from "List" sheet and an array for putting to the sheet is created. And also, the array is put to "List" sheet.
Note:
When you try to use this script without enabling V8 runtime, an error like Syntax error occurs. Please be careful about this.
This modified script is for your sample Spreadsheet. If your actual Spreadsheet is differnt structure from your provided sample one, this modified script might not be able to be used. Please be careful about this.
If you cannot use V8 runtime, please test the following modified script.
function example2b() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var list = ss.getSheetByName("List");
var data = ss.getSheetByName("Data");
var obj = data.getRange("C2:E" + data.getLastRow()).getValues()
.reduce(function (o, [a, b, c]) {
o[a] = [b, c];
return o
}, {});
var range = list.getRange("B2:B" + list.getLastRow());
var values = range.getValues().map(function ([b]) { return obj[b] || [null, null] });
range.offset(0, 1, values.length, 2).setValues(values);
}
References:
getValues()
setValues(values)
reduce()
map()

How to extract few cells from a Google Sheet and copy to another sheet using a single statement

using JavaScript to access google sheets via google API.
I am copying the entire content of a "google spreadsheet" "Parent sheet" into a two dimension array vaParent
var ssParent = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Parent");
var vaParent = ssParent.getRange(2, 1, ssParent.getLastRow(), ssParent.getLastColumn() ).getValues();
I am extracting 6 array elements
var vaNewParent01 = vaParent[i].slice(3, 9);
Now, I would like to copy vaNewParent01 to a new google "spreadsheet" "company sheet"
ssCompany.getRange( 2, 1 ).setValues( vaNewParent01 );
above code does not work??
I've found that vaNewParent01 is a single dimension array with 6 elements
however, ssCompany.getRange( 2, 1 ).setValues( vaNewParent01
only works if vaNewParent01 is an array of array
question:
a/ how to extract few cells from a sheet and copy to another using a single statement.
thanks in advance for any help
cheers
From your showing script, if your script is like the following script,
var ssParent = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Parent");
var vaParent = ssParent.getRange(2, 1, ssParent.getLastRow(), ssParent.getLastColumn()).getValues();
for (var i = 0; i < vaParent.length; i++) {
var vaNewParent01 = vaParent[i].slice(3, 9);
ssCompany.getRange(2, 1).setValues(vaNewParent01);
}
vaNewParent01 is 1 dimensional array. In order to use setValues(values), values is required to be 2 dimensional array. And about ssCompany.getRange( 2, 1 ).setValues( vaNewParent01 );, in this case, please include the numbers of rows and columns of the values you want to put. If my understanding of your situation is correct, I think that the reason for your issue is due to this.
About how to extract few cells from a sheet and copy to another using a single statement., in this case, how about the following sample script?
Sample script 1:
This script is modified from your showing script by guessing your tested script.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssParent = ss.getSheetByName("Parent");
var ssCompany = ss.getSheetByName("sample"); // Please set the destination sheet name.
var vaParent = ssParent.getRange(2, 1, ssParent.getLastRow(), ssParent.getLastColumn()).getValues();
var values = [];
for (var i = 0; i < vaParent.length; i++) {
var vaNewParent01 = vaParent[i].slice(3, 9);
values.push(vaNewParent01);
}
ssCompany.getRange(2, 1, values.length, values[0].length).setValues(values);
Sample script 2:
As other sample script, how about the following script? In this sample script, copyTo of Class Range is used. From your showing script, I thought that you wanted to copy the values from D2:I of the source sheet to A2 of the destination sheet.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssParent = ss.getSheetByName("Parent");
var ssCompany = ss.getSheetByName("sample"); // Please set the destination sheet name.
var src = ssParent.getRange("D2:I" + ssParent.getLastRow());
src.copyTo(ssCompany.getRange(2, 1), { contentsOnly: true });
References:
setValues(values)
copyTo(destination, options)
arr is single dimension array
let vs = arr.map(e => [e]); //for a column;
let vs = [arr]; //for a row
Sheet.getRange(1,1,vs.length, vs[0].length).setValues(vs);

two active sheets in google sheets update error based on cell comparison

I'm trying to get this code working but I know I'm missing something. what the code should do is: to find a specific text from another cell using another sheet reference, so there are two sheets in the google sheet below:'BSR DATA' and 'ENTRY FORM'. from ENTRY FORM,cell g6 will compare its data to the first column of BSR DATA and if it meets the criteria, it will update the specific row of the array/cell reference with "new value".
link to google sheets:https://docs.google.com/spreadsheets/d/1kBuczydffPFBEfy2oegC1WsKXMjSxbGNRzWLCKurOMs/edit?usp=drivesdk
function UpdateBSRSpecial()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var testForm = ss.getSheetByName("ENTRY FORM");
var testTable = ss.getSheetByName("BSR DATA");
var testFormValue = testForm.getRange("G6").getValue();
var rangeData = testTable.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
for(var i=0;i>lastRow;i++){
var dataID = testForm.getRange(i,1).getValue();
if(testFormValue == dataID)
{
testTable.getRange(i,6).setValue("new value");
};
};
};
The existing script is updating all rows because it loops with for(var i=0;i>lastRow;i++){. The following script demonstrates the process to search for a discrete term.
I am not going to deal with the updating of the "new value" since that is not clear from the question.
Aspects to note
the script gets all the data on BSR - var bsrdata = bsr.getRange(1,1,bsrLR,bstLC).getValues();. This can be used later when updating values.
using the Javascript map method, Column A is extracted as a separate array for the search - var bsrColA = bsrdata.map(function(e){return e[0];});//[[e],[e],[e]]=>[e,e,e]
the search is conducted using the Javascript indexOf method. Note: if a match is found, the method returns the index number of the search term within the array; if no match is found, the method returns "-1".
the script tests to see if the search was successful: if (result !=-1){
indexOf returns a zero-based index, therefore the actual row number is the index plus one: var row = result+1;
function so5866895401() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formsheetname = "ENTRY FORM";
var form = ss.getSheetByName(formsheetname);
var bsrname = "BSR DATA";
var bsr = ss.getSheetByName(bsrname);
// get input cell on form
var searchterm = form.getRange("G6").getValue();
// Logger.log(searchterm);// DEBUG
//get data from BSR
var bsrLR = bsr.getLastRow();
var bstLC = bsr.getLastColumn();
var bsrdata = bsr.getRange(1,1,bsrLR,bstLC).getValues();
// get column A of BSR
var bsrColA = bsrdata.map(function(e){return e[0];});//[[e],[e],[e]]=>[e,e,e]
// Logger.log(bsrColA);// DEBUG
// search for the searchterm in ColumnA of BSR
var result = bsrColA.indexOf(searchterm); // zero-based
if (result !=-1){
var row = result+1;
Logger.log("result = "+result+", row = "+row);
}
}

Google Script: getValues() and setValues() for rectangular grid

In Google Sheets, when I store a given a1notation into an appendRow, I want the values from this appended Row to be shown on another sheet while retaining the grid structure it had when it was saved intitially.
So from the below code, the data from the appendedRow(11,5,1,5) shall be set to the grid I5:J7.
Sadly I am not proficient enough to work with for loops and push() / array, so I would appreciate your support greatly.
Thank you.
function Test() {
var rs = SpreadsheetApp.getActiveSpreadsheet();
var ss = rs.getSheetByName("Sheet");
var tempArray = [ss.getRange(11,5,1,5)]
var values = tempArray.getValues();
ss.getRange('I5:J7').setValues(values);
}
Google Apps Script documentation has a Reference section which gives you a brief explanation and example of different methods. Under
https://developers.google.com/apps-script/reference/spreadsheet/range#setvaluesvalues
you can see that the method setValues(values) requires the dimension of the origin and destination range to match. Thus, you cannot paste a 1x5 range into a 2x3 range.
What you can do is to loop through the cells of the destination range and to assign them sequentially a value from the origin range - until either all the values have been passed or the destination range is full. You could implement it like this:
function Test() {
var rs = SpreadsheetApp.getActiveSpreadsheet();
var ss = rs.getSheetByName("Sheet");
var values = ss.getRange(11,5,1,5).getValues();
var destination=ss.getRange('I5:J7');
var value=0;
for(i=1;i<=3;i++)
{
for(j=1;j<=2;j++)
{
var cell=destination.getCell(i,j);
if(typeof values[0][value] !== "undefined")
{
cell.setValue(values[0][value]);
}
value=value+1;
}
}
}

Google Apps Script Replace and update cell within a range

I have a Google spreadsheet that I'm trying to remove the word "woo" within a range of cells
So far I've managed to loop through the results and log the results, however I haven't figured how to update that information in the spreadsheet itself.
Any guidance would be welcomed
Thank you
function myFunction () {
var ss = SpreadsheetApp.getActiveSheet().getRange('B:B')
var data = ss.getValues();
for (var i = 0; i < data.length; i++) {
var text = data[i].toString();
var finaltext = text.replace(/woo/g, "");
data[i] = finaltext;
Logger.log(data[i]);
}
}
Use setValues()
Notes:
Usually ss is used as a shorthand for spreadsheet, as it's used on the code for a range it's better to use range as a variable name.
setValues() returns a 2D array, so data[i] returns an array of row values rather than a cell value. To get/set cell values, use data[i][0] notation.
Considering the above replace
var ss = SpreadsheetApp.getActiveSheet().getRange('B:B')
by
var range = SpreadsheetApp.getActiveSheet().getRange('B:B')
then add the following line after the for block.
range.setValues(data);
Regarding text var declaration, replace
var text = data[i].toString();
to
var text = data[i][0].toString();
Using open ended references like B:B could lead to problems. To avoid them be sure to keep the sheet rows at minimum or better instead of using an open ended reference use something like B1:B10.

Resources