how to resolve google app scripting error: "TypeError: Cannot call method "getRange" of null. (line 16, file "Code")" when debugging the following script:
function getEmails_(q) {
var emails = [];
var threads = GmailApp.search(q);
for (var i in threads) {
var msgs = threads[i].getMessages();
for (var j in msgs) {
emails.push([msgs[j].getBody().replace(/<.*?>/g, '\n')
.replace(/^\s*\n/gm, '').replace(/^\s*/gm, '').replace(/\s*\n/gm, '\n')
]);
}
}
return emails;
}
function appendData_(sheet, array2d) {
sheet.getRange(sheet.getLastRow() + 1, 1, array2d.length, array2d[0].length).setValues(array2d);
}
function saveEmails() {
var array2d = getEmails_("Still looking for Python Programming help");
if (array2d) {
appendData_(SpreadsheetApp.getActiveSheet(), array2d);
}
}
Have you set the sheet variable?
var sheet = SpreadsheetApp.getActiveSheet();
Do Logger.log(array2d); to check the data is being set in the array.
It'll be because it cant find the sheet.
I sometimes had this error and had to rename the sheet and it worked. It's strange.
You are trying to access getRange() on a null sheet because in your context SpreadsheetApp.getActiveSheet() return null (ie. there is no active spreadsheet)
More context could help to find a way to provide an active Spreadsheet to your function.
You can by instance open a spreadsheet with :
SpreadsheetApp.open(file)
SpreadsheetApp.openById(id)
SpreadsheetApp.openByUrl(url)
And then set it active SpreadsheetApp.setActiveSpreadsheet(newActiveSpreadsheet)
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app
There is other ways to make a spreadsheet active, for example when a trigger is fired (ex. notifyOnFormSubmit).
To ensure you have an active spreadsheet, you can test SpreadsheetApp.getActiveSheet() before using it :
var spreadsheet = SpreadsheetApp.getActiveSheet();
if (spreadsheet) {
Logger.log('spreadsheet url:' + spreadsheet.getUrl());
}
Related
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()
I've been trying to write a script that simply takes a filtered data, copies it, and then pastes it into another sheet. Nothing I seem to do works. With the code below, which I found online, it should work, but I keep getting an error that states The number of rows in the range must be at least 1. However, I have data in the range A7:R500 and I'm only filtering out blanks and 'W'. Am I correct in this thinking?
function copyPaste(){
var sheet = SpreadsheetApp.getActiveSheet();
var values = sheet.getRange('A7:R500').getValues();
var hiddenValues = ['', 'W'];
values = values.filter(function(v){
return hiddenValues.indexOf(v[4]) == 'W';
});
sheet.getRange(1,21, values.length, 18).setValues(values);
}
Solution:
Since you are already using a filter Array, you can compare hiddenValues.indexOf(v[4]) to -1 to filter out blanks and "W".
Also, since your goal is to paste the results in a different sheet, you need to define both the source and the destination sheet. Create a sheet and plug its name into the new sheet name tag in the code below.
Sample Code:
function copyPaste() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getActiveSheet();
var sheet2 = ss.getSheetByName('<new sheet name>');
var values = sheet1.getRange('A7:R500').getValues();
var hiddenValues = ['', 'W'];
values = values.filter(function(v){
return hiddenValues.indexOf(v[4]) == -1;
});
sheet2.getRange(1,21, values.length, 18).setValues(values);
}
Reference:
indexOf()
In Google Sheets, I have a sidebar using html, with a form which runs processForm(this) upon submission. The form was created based on the headings in the sheet, which is why I am using the headings to retrieve the values from the form. The code seems to work fine until I try to use setValues(). There is no error, but nothing seems to happen at that line. Please let me know what I might be doing wrong. Thanks.
function processForm(formObject) {
var headers = getHeaders();
var newRow = [];
for (var i = 0; i < headers.length; i++) {
newRow.push(formObject["" + headers[i]]); // TODO: convert objects to appropriate formats
}
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(parseInt(formObject.row)+1, 1, 1, headers.length)
Logger.log(JSON.stringify(newRow)); // example output: ["John","Smith","male","6615554109","","example_email#yahoo.com"]
range.setValues(newRow); // values not getting set
}
Change last line:
range.setValues([newRow]);
(thanks for the solution, Serge insas!)
I like to copy multiple rows value from and to the same google sheet by a script, but I failed to get it to work.
My script:
function Copymultiplerows() {
var ss = SpreadsheetApp.openByUrl("Spreadsheeturl");
Logger.log(ss.getName());
var mysheet = ss.getSheetByName('Sheet1');
var source = ss.getRange ('72:350');
var tss =SpreadsheetApp.openByUrl("Spreadsheeturl");
var ts = tss.getSheetByName('Sheet1');
ts.getRange('73:351');
}
You also need to get and set the values
ts.getRange("73:351").setValues(source.getValues());
I have been using Google Apps Script along with spreadsheet to create a spreadsheet that watches a set of stocks. I have assigned each stock it's own sheet and set up the function with a day trigger so that it refreshes all of the information each day. I spent so much time debugging a lot and finally got it working perfectly for the first two sheets. I now added 3 more and it's not doing anything for them.
function XMLDATAONDAY() {
for (r=0;r<5;r++){
var ss=SpreadsheetApp.getActiveSpreadsheet()
var sheets= ["HSY","AAPL","CENX","MSFT","TSLA"]
var sheet=ss.getSheetByName(sheets[r])
var i=14
var dateSrc=sheet.getRange(2,5)
var stockPrice = sheet.getRange(5,4).getValue()
var displayCell= sheet.getRange(2,4)
var date = dateSrc.getValue()
SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1IOBQpdUr0fq_clie-0AOnCAN87qTy9Yn1h79akMJ7uc/edit#gid=0');
for (i=14;i<366;i++) {
var sheets= ["HSY","AAPL","CENX","MSFT","TSLA"]
var stockCell=sheet.getRange(i,2)
var dateCell=sheet.getRange(i,1)
if(stockCell.getValue()== ""){
sheet.getRange(14,1).copyFormatToRange(sheet, 1, 1, i, i)
sheet.getRange(14,2).copyFormatToRange(sheet, 2, 2, i, i)
dateCell.setValue(date);
stockCell.setValue(stockPrice);
i=400;
}
}
}
}
It's probably something that I'm just overlooking, but I just can't seem to find anything.
This line:
i=400;
Should probably be changed to:
break;
I'm guessing that you are setting i to 400 in order to stop the next loop from running?
There is a duplicate variable assignment for sheets. It's an array literal. Only define that once, and put it outside of the for loops. Put the variable assignment for ss outside of the loop also. If you do not assign r with a statement, the code still runs without any noticeable difference, but without a var statement, the variable is defined in the global scope.
The statement to open a spreadsheet by URL is not doing anything. It's not assigned to a variable, so I'm not sure what that line is for.
function XMLDATAONDAY() {
var date,dateCell,dateSrc,displayCell,i,r,sheet,sheets,ss,stockCell,stockPrice;
sheets = ["HSY","AAPL","CENX","MSFT","TSLA"];
ss=SpreadsheetApp.getActiveSpreadsheet();
r=0;
for (r=0;r<5;r++){
sheet=ss.getSheetByName(sheets[r])
i=14
dateSrc=sheet.getRange(2,5)
stockPrice = sheet.getRange(5,4).getValue()
displayCell = sheet.getRange(2,4)
date = dateSrc.getValue()
//SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/sheet_ID_HERE/edit#gid=0');
for (i=14;i<366;i++) {
stockCell=sheet.getRange(i,2)
dateCell=sheet.getRange(i,1)
if(stockCell.getValue()== ""){
sheet.getRange(14,1).copyFormatToRange(sheet, 1, 1, i, i)
sheet.getRange(14,2).copyFormatToRange(sheet, 2, 2, i, i)
dateCell.setValue(date);
stockCell.setValue(stockPrice);
break;
}
}
}
}