Get 'cell reference out of range ' in Google Apps Script - google-apps-script

So I've created a program that is supposed to mark one of multiple columns when the if conditions are met, so we can categorize our marketing sectors.
If you look at the last line of code you will see sheet (which encapsulates everything) getRange (which defines the range that the next part,getCell should be inside of ) and then you will see getCell.
I added +1 to the get cell Variable because it is not considered an array and therefore doesn't start with zero, which means it an sum of 4 would start in fourth row (as opposed to the 5th row for arrays).The problem is that I get a "Cell reference out of range and google script editor will highlight the aforementioned row.
If the get range is within the sheet, and the getCell is within the getRange I dont know how this could happen.
function myFunction() {
var app = SpreadsheetApp;
var sheet = app.getActiveSpreadsheet().getActiveSheet();
var lr= sheet.getLastRow();
var dataRangeContact = sheet.getRange("B4:B"+lr);
var dataContact = dataRangeContact.getValues();
var dataRangeComp= sheet.getRange("M4:M"+lr);
var dataComp=dataRangeComp.getValues();
var IndigoColumn= sheet.getRange("L4:L"+lr);
var validIndigoColumn =12;
var validLabColumn=11;
var validDesColumn=10;
var validPocColumn=9;
var validArtColumn=8;
for(var i=3; i<=lr; i++) {
var lr=sheet.getLastRow();
var rowTwo=dataContact[i].toString();
var rowThirteen=dataComp[i].toString();
if (rowThirteen=="University Lab") {
if (rowTwo.match(/(process engineer|analytical chemist|scientist|sr.scientist|research scientist|test Engineer|Medical Devices professional|scientific)/i)) {
sheet.getRange(4,12,lr).getCell(i+1,validIndigoColumn).setValue("Y");
}
}
}}

I think this is the same function rewritten a little differently. It will most likely run faster. It only has one range so it may be easier to avoid range errors. Keep in mind that rows and columns start at 1 and array indices start at 0.
I sometimes do this sort of thing to keep them straight.
Anyway here's my shortened version of the function:
function myFunction(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var vA=rg.getValues();
for(var i=3;i<vA.length;i++){
if(vA[i][12]=='University Lab' && vA[i][1].toString().match(/(process engineer|analytical chemist|scientist|sr.scientist|research scientist|test Engineer|Medical Devices professional|scientific)/i)){
vA[i][11]='Y';
}
}
rg.setValues(vA);//rewrites the data into the range including all changes all at one time
}
I have no way to test it.

Related

Adding a row to a named range

I have a named range in Google Sheets (A1:K14). All I'm trying to do is add a new row at the bottom of the named range. It seems like an easy task. The named range doesn't expand using this code and I don't get an error message. It does insert a new row outside of the named range, which is not what I'm trying to do. If I change to insertRowBefore (lastRow), the new blank row is inserted and the named range is expanded. I'm teaching myself GAS, so this is probably a simple mistake on my part
var sheet = SpreadsheetApp.getActiveSheet()
var Range = sheet.setActiveRange(range);
var data = Range.getValues();
Logger.log(range.getA1Notation());
var lastRow = SpreadsheetApp.getActiveSheet().getRange('DataRange').getNumRows();
SpreadsheetApp.getActive().insertRowAfter(lastRow);
return range;
Adding rows and/or columns to a namedRange
This function has the defaults setup to add one row.
//r is number of rows to add
//c is number of columns to add
function addRowAndColumnsToNamedRange(name,r,c) {
var name=name||'One';//default name
var r=r||1;//default number of rows to add
var c=c||0;//default number columns to add
var ss=SpreadsheetApp.getActive();
var nrgA=ss.getNamedRanges();
for(var i=0;i<nrgA.length;i++) {
if(nrgA[i].getName()==name) {
var nr=nrgA[i];
var h=nr.getRange().getHeight();
var w=nr.getRange().getWidth();
var sh=nr.getRange().getSheet();
var row=nr.getRange().getRow();
var col=nr.getRange().getColumn();
var rg=sh.getRange(row,col,h+r,w+c);
ss.setNamedRange(name,rg);
break;
}
}
}
Class NamedRange
Class Range
I think one of the approach can be to get that NamedRange and expand it explicitly.
Something like this:
Get that NamedRange using getNamedRanges() :
Set range of that NamedRange including new row using setRange(range)

Google Sheets appendRow with formula values

My goal is to write a script in Google Sheets that will copy two (or more) values from specific cells on a single row on a worksheet to the bottom of the list on another. I need to copy the actual values as well as calculate additional from the original values.
This is my first attempt and should explain what I am trying to do:
function CopyCals() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Sheet2'), true);
spreadsheet.appendRow(['=today()','','=Sheet1!B30','=Sheet1!E30','=Sheet1!B30-Sheet1!E30','=Sheet1!B30-(Sheet1!E30-2500)']);
};
This works great, except it appends the actual formulas, rather than the values of the fields. As the fields change, so do the values of what was copied.
To work around this, I tried getRange but I can't figure out how to select specific elements (i.e. the values of C30 and E30) in the array. Below is what I tried:
function copyCals2() {
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange("B30:E30").getValues();
spreadsheet.appendRow(range[0]);
};
How do I get appendRow to only print the values for C30 and E30 and use them in formulas as shown in my original attempt.
Getting the desired data
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
};
This is probably easier to write:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var b30=sh1.getRange('B30').getValue();
var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
I would probably prefer doing it something like this:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getRange('B30:B31').getValues();
var b30=vA[0][0];
var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
If you had a lot of data and it's all grouped together nicely then the later can save you lot's of time because you only have to do one read to get all of the data at one time.
In the latter approach its helpful to use your debugger to help you to get a picture of what the data looks like:
vA=[[value in B30],[value in B31]] so the vA[0][0] is B30 and vA[1][0] is B31
And here is yet another way to write the getRange() function:
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getRange(30,2,2,1).getValues();
//var vA=sh1.getRange('B30:B31').getValues();
var b30=vA[0][0];
var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
And just to make matters even a little more complicated for you a lot of times it's helpful to get the entire page of values all at one time so in that case this is what you left with.
function CopyCals() {
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet1');
var sh2=ss.getSheetByName('Sheet2');
var vA=sh1.getDataRange().getValues();
var b30=vA[29][1];
var b31=vA[30][1];
//var vA=sh1.getRange('B30:B31').getValues();//gets all the data at one time
//var b30=vA[0][0];
//var b31=vA[1][0];
//var b30=sh1.getRange('B30').getValue();
//var e30=sh1.getRange('E30').getValue();
sh2.appendRow([new Date(),'',b30,e30,b30-e30,b30-(E30-2500)]);
//sh2.appendRow([new Date(),'',sh1.getRange('B30').getValue(),sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-sh1.getRange('E30').getValue(),sh1.getRange('B30').getValue()-(sh1.getRange('E30').getValue()-2500)]);
}
All of these function do the same thing you can pick which one you want depending upon what your trying to accomplish.
Is there a way to do this if the row you are appending is not known?
For example I want to append a row with mixed data and a formula as such:
var values = [today, record['Date'], record['EventType'], record['Display Name'],
'=A?'];
var ss = SpreadsheetApp.openById(sponsorSheetId);
var sheet = ss.getSheetByName('SentLog');
sheet.appendRow(values);
Where, in A?, ? is the last row. But since I haven't appended yet, I wouldn't know what the last row is. And this needs to be a formula that refers to cell in column A.

google-script - hide/show a dynamic range of columns

I have a spreadsheet where every day a new column gets added with:
sheet.insertColumnAfter(6)
now i want to show/hide the columns starting at column(7) up till the last column of the sheet (which is a dynamic value).
i had the following thought and no idea why it doenst work:
function Historyausblenden() {
var bereich = SpreadsheetApp.getActive();
var letzte = bereich.getLastColumn();
bereich.getRange("G",letzte).activate();
bereich.getActiveSheet().hideColumns(bereich.getActiveRange().getColumn(), bereich.getActiveRange().getNumColumns());
bereich.getRange('F:F').activate();
}
It seems that getRange doesnt accept var letzte.
The errorcode says: "Methode getRange(string,number) not found!"
Why? What does that mean?
Several issues in your code.
You can't call getLastColumn() on a spreadsheet.
String concatenation in javascript is most easily done with + (e.g. "G" + letzte).
You can't activate a range without first selecting the sheet.
Lastly, you don't really need to activate to accomplish what you're trying to do, but you may have another reason for doing so.
Try this instead (and make sure you're selecting the correct sheet in the third line):
function hideHistory() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("Sheet1");
var lastColumn = sheet.getMaxColumns();
var startColumn = 7;
sheet.hideColumns(startColumn, lastColumn-startColumn+1);
}

Improving Apps Script flexibility by using a column of sheet data instead of hard-coded IDs

Background: My coworkers originally each had a worksheet within the same Google Sheets file that makes a lot of calculations (and was getting unusable). Now, everyone has their own (known) Google Sheets file. To run the same calculations, we need to consolidate all that data into a master sheet (image ref below). We tried =importrange(...), but it's too heavy and breaks often (i.e., Loading... and other unfilled cells).
I've written some code to do this import, but right now its only manual: manually repeating the code and manually add the sheet IDs and changing the destrange.getRange(Cell range) each time. We have 80+ analysts, and fairly high turnover rates, so this would take an absurd amount of time. I'm new to Sheets and Apps Script, and know how to make the script use a cell as reference for a valid range or a valid ID, but I need something that can move a cell down and reference the new info.
Example:
Sheet 1 has a column of everyone Sheet ID
Script Pseudocode
get first row's id(Row 1), get sheet tab, get range, copies to active sheet's corresponding row(Row 1).
gets second row's id(Row 2), get sheet tab, get range, copies to active sheet's corresponding row (Row 2)
etc.
My script understanding is way to low to know how to process this. I have no idea what to read and learn to make it work properly.
function getdata() {
var confirm = Browser.msgBox('Preparing to draw data','Draw the data like your french girls?', Browser.Buttons.YES_NO);
if(confirm == 'yes'){
// I eventually want this to draw the ID from Column A:A, not hard-coded
var sourcess = SpreadsheetApp.openById('1B9sA5J-Jx0kBLuzP5vZ3LZcSw4CN9sS6A_mSbR9b26g');
var sourcesheet = sourcess.getSheetByName('Data Draw'); // source sheet name
var sourcerange = sourcesheet.getRange('E4:DU4'); // range
var sourcevalues = sourcerange.getValues();
var ss = SpreadsheetApp.getActiveSpreadsheet(); //
var destsheet = ss.getSheetByName('Master Totals'); //
// This range needs to somehow move one down after each time it pastes a row in.
var destrange = destsheet.getRange('E4:DU4');
destrange.setValues(sourcevalues); // Data into destsheet
}
}
Any suggestions are greatly appreciated!
Thanks to tehhowch for pointing me in the right direction!
function getdata() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master Totals');
var confirm = Browser.msgBox('Drawing Data','Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO);
if(confirm =='yes'){
var lr = ss.getLastRow();
for (var i = 4; i<=lr; i++) {
var currentID = ss.getRange(i, 1).getValue();
var sourcess = SpreadsheetApp.openByUrl(currentID);
var sourcesheet = sourcess.getSheetByName('Data Draw');
var sourcerange = sourcesheet.getRange('E4:DU4');
var sourcevalues = sourcerange.getValues();
var destrange = destsheet.getRange('E' +i+':'+ 'DU'+ i);
destrange.setValues(sourcevalues);
I just had to learn how to use a variable loop.
Edit: thanks also to Phil for making my question more presentable!
Now that you've figured out one way to do it, I'll offer an alternative that uses batch methods (i.e. is much more time- and resource-efficient):
function getData() {
var wb = SpreadsheetApp.getActive();
var ss = wb.getActiveSheet();
var dest = wb.getSheetByName('Master Totals');
if (!dest || "yes" !== Browser.msgBox('Drawing Data', 'Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO))
return;
// Batch-read the first column into an array of arrays of values.
var ssids = ss.getSheetValues(4, 1, ss.getLastRow() - 4, 1);
var output = [];
for (var row = 0; row < ssids.length; ++row) {
var targetID = ssids[row][0];
// Open the remote sheet (consider using try-catch
// and adding error handling).
var remote = SpreadsheetApp.openById(targetID);
var source = remote.getSheetByName("Data Draw");
var toImport = source.getRange("E4:DU4").getValues();
// Add this 2D array to the end of our 2D output.
output = [].concat(output, toImport);
}
// Write collected data, if any, anchored from E4.
if(output.length > 0 && output[0].length > 0)
dest.getRange(4, 5, output.length, output[0].length).setValues(output);
}
Each call to getRange and setValues adds measurable time to the execution time - i.e. on the order of hundreds of milliseconds. Minimizing use of the Google interface classes and sticking to JavaScript wherever possible will dramatically improve your scripts' responsiveness.

Unmerge in Google Apps Script

I'd like to unmerge all the cells of my google spreadsheet using script. I believe that VBA has this option (cells.unmerge) but I can't find a similar operation in GAS. I've tried this script but it didn't seem to work.
function MyFunction() {
var Sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var Range = Sheet.getDataRange().activate();
Range.clearFormat();
}
The correct word is "Range.breakApart" not "unmerge". Note that this only works when the range it is called on encompasses all merged cells.
Try this code:
function unmerge() {
var app = SpreadsheetApp;
// get current active sheet use single line coding
var activeSheet =app.getActiveSpreadsheet().getActiveSheet();
// get last row
var lstrow= activeSheet.getLastRow();
// see below description **
var mergerange = activeSheet.getRange(13,4,lstrow).getMergedRanges();
for (var i = 0; i < mergerange.length; i++) {
Logger.log(mergerange[i].getA1Notation());
Logger.log(mergerange[i].getDisplayValue());
mergerange[i].breakApart();
}
}
** 13= start row number. 4 = column number of merge cells.