Adding a CellImage from One Google Sheet to Another - google-apps-script

Similar to a previous question, I have been trying to add a CellImage created with a formula in one Google Sheet and putting it into another Google Sheet.
I have a CellImage created in the first sheet with the formula:
=image("https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl="&B1)
The following is an Apps Script I made that should take the CellImage from the first sheet and insert it into a cell on the second sheet:
function myFunction() {
let doc = DocumentApp.create("QR Holder");
let sheet = SpreadsheetApp.getActiveSpreadsheet();
let sheet1 = sheet.getSheetByName('Sheet 1');
let sheet2 = sheet.getSheetByName('Sheet 2');
sheet2.getRange(1,1).setValue(sheet1.getRange(1,1).getValue());
}
The execution log says that for the last line:
TypeError: Cannot read properties of null (reading 'getRange')
This shows that the getValue() used on the image in Sheet 1 is null.

About your error of TypeError: Cannot read properties of null (reading 'getRange'), in this case, it is considered that your sheet names of Sheet 1 or Sheet 2 might not be existing. So, please confirm your sheet names again.
And, if a formula of =image("https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl="&B1) is put in a cell "A1" of "Sheet 1" sheet using the valid sheet names, and when you want to copy the cell value to the cell "A1" of "Sheet 2", unfortunately, I'm worried that an error like Exception: Service error: Spreadsheets might occur at sheet2.getRange(1,1).setValue(sheet1.getRange(1,1).getValue());.
So, in this answer, in order to copy the cell value, I would like to propose the following modification.
Modified script:
Before you use this script, please confirm your sheet names again.
function myFunction() {
// let doc = DocumentApp.create("QR Holder"); // In your script, it seems that this is not used.
let sheet = SpreadsheetApp.getActiveSpreadsheet();
let sheet1 = sheet.getSheetByName('Sheet 1');
let sheet2 = sheet.getSheetByName('Sheet 2');
sheet1.getRange(1, 1).copyTo(sheet2.getRange(1, 1), { contentsOnly: true });
}
If you want to copy the formula, please modify sheet1.getRange(1, 1).copyTo(sheet2.getRange(1, 1), { contentsOnly: true }); to sheet1.getRange(1,1,1,2).copyTo(sheet2.getRange(1,1));. By this, the formula and the value of "B1" are copied.
Reference:
copyTo(destination, options)

This runs for me:
function myFunction() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet1 = ss.getSheetByName('Sheet1');
let sheet2 = ss.getSheetByName('Sheet2');
sheet2.getRange(1,1).setValue(sheet1.getRange(1,1).getValue());
Logger.log(sheet2.getRange("A1").getValue());
}

Related

Google sheet script to update cell value in another sheet

I'm trying to use a code I found in another topic but so far I failed. I'm trying to find the corresponding row and update the last column in another google spreadsheet after updating the first column of another spreadsheet.
When the user selects "ready" in ColC of spreadsheet X, I need to look up the ID value in ColB on another sheet (Y). Then I need to access spreadsheet Y and find the row that contains that same ID. Access the last column or columnC (3) and change the cell value to "ready".
Here is what I have so far but I get the error:
TypeError: Cannot read properties of undefined (reading 'range')
Can anyone help me fix this?
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
if( s.getName() == "Studio"){
if ( (e.range.getColumn() == 2.0) && (e.range.getValue() == "ready") ){
var nextCell = e.range.offset(0, -1);
var buybackId = nextCell.getValue();
var ss2 = SpreadsheetApp.openById('1bbNuH_GXbmNwMHk7Kjo0zAhD-QGwQqiva8_HJc3mgFY');
var sheet = ss2.getSheetByName("Status");
var data = sheet.getDataRange().getValues();
for(var i = 0; i<data.length;i++){
if(data[i][1] == buybackId){
sheet.getRange((i+1), 3).setValue("ready");
}
}
}
}
}
The table structure looks like this:
In the 1st section of your question, you say I'm trying to find the corresponding row and update the last column in another google spreadsheet after updating the first column of another spreadsheet.. But, from your 2nd section, it seems that you wanted to run the script by editing column "B" like ready. By the way, about When the user selects "ready" in ColC of spreadsheet X,, your selects is to edit?
So, when column "B" in "Studio" sheet on the active Spreadsheet is edited to like ready, you want to update column "C" of "Status" sheet in another Spreadsheet by checking the ID of column "B".
If my understanding is correct, how about the following modification?
Modification points:
In order to use SpreadsheetApp.openById, it is required to use the installable OnEdit trigger.
About your current error of TypeError: Cannot read properties of undefined (reading 'range'), I'm worried that you might have directly run the function onEdit with the script editor. In that case, such an error occurs because of no event object.
When these points are reflected in your script, how about the following modification?
Modified script:
Please copy and paste the following script to the script editor of Spreadsheet. And, please install OnEdit trigger to installedOnEdit. When you use this script, please edit the column "B" of "Studio" sheet of the active Spreadsheet. By this, the script is run. And, please set your destination Spreadsheet ID to const ss = SpreadsheetApp.openById('###');.
function installedOnEdit(e) {
const { range } = e;
const sheet = range.getSheet();
if (sheet.getSheetName() != "Studio" || range.columnStart != 2) return;
const [id, status] = range.offset(0, -1, 1, 2).getDisplayValues()[0];
const ss = SpreadsheetApp.openById('###'); // Please set the destination Spreasheet ID.
const dstSheet = ss.getSheetByName("Status");
const r = dstSheet.getRange("B2:B" + dstSheet.getLastRow()).createTextFinder(id).matchEntireCell(true).findNext();
if (!r) return;
r.offset(0, 1).setValue(status);
}
When this script is run by the installable OnEdit trigger, the above goal can be achieved.
Note:
If you want to copy all values from the source sheet to the destination sheet, how about the following sample script? In this case, you can directly run the script with the script editor.
function sample() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Studio");
const obj = sheet.getRange("A2:B" + sheet.getLastRow()).getDisplayValues().reduce((o, [a, b]) => (o[a] = b, o), {});
const ss = SpreadsheetApp.openById('###'); // Please set the destination Spreasheet ID.
const dstSheet = ss.getSheetByName("Status");
const dstRange = dstSheet.getRange("B2:C" + dstSheet.getLastRow());
const dstValues = dstRange.getValues().map(([b, c]) => [obj[b] || c]);
dstRange.offset(0, 1, dstValues.length, 1).setValues(dstValues);
}
Note:
About the 1st script, when you directly run the script, an error like TypeError: Cannot destructure property 'range' of 'e' as it is undefined. occurs. Please be careful about this. The 1st script is automatically run with the installable OnEdit trigger.
By the way, when you use the 1st script, please rename your onEdit function name. Because when you didn't rename it, when the cell is edited, both functions installedOnEdit and onEdit are run. Please be careful about this.
Reference:
Installable Triggers

Is there a way to import cells in a certain range for a large number of sheets

I have 25 tabs in a Google Spreadsheet, but this number slowly grows as more data is added. The importrange function that we are provided by default requires the specification of which sheets to import data from using something like what was outlined here: Combining multiple spreadsheets in one using IMPORTRANGE. However, is there a way (using a default function or the Apps Script) to do this for all tabs in the Google Spreadsheet, without specifying each one individually?
I believe your goal is as follows.
You want to retrieve the values from all sheets in a Google Spreadsheet and want to put the values to a sheet of the destination Spreadsheet. In this case, you want to set a range you want to retrieve.
You want to achieve this using Google Apps Script.
Sample script:
Please copy and paste the following script to the script editor of Google Apps Script, and set the variables. And, run the function.
function myFunction() {
const range = "A1:E3"; // Please set the range you want to retrieve.
const srcSpreadsheetId = "###"; // Please set source Spreadsheet ID.
const dstSpreadsheetId = "###"; // Please set destination Spreadsheet ID.
const dstSheetName = "Sheet1"; // Please set destination sheet name.
const values = SpreadsheetApp.openById(srcSpreadsheetId).getSheets().flatMap(s => s.getRange(range).getValues());
const dstSheet = SpreadsheetApp.openById(dstSpreadsheetId).getSheetByName(dstSheetName);
dstSheet.clearContents().getRange(1, 1, values.length, values[0].length).setValues(values);
}
When this script is run, the values are retrieved from the specific range of all sheets from the source Spreadsheet, and the values are put to the destination sheet.
In the above script, the retrieved values to "A1" of the destination sheet. If you want to append the values, please modify as follows.
From
dstSheet.clearContents().getRange(1, 1, values.length, values[0].length).setValues(values);
To
dstSheet.getRange(dstSheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
Unfortunately, I'm not sure whether I could correctly understand your question. So, for example, if you want to retrieve the values from all sheets except for multiple sheets, and you want to put the retrieved values to the specification sheet in the same Spreadsheet, how about the following sample script? In this case, you can use this script as a custom function. So, when you use this script, please put a custom function of =SAMPLE("A1:E3", "Sheet2,Sheet3") to a cell. In this sample arguments, the values are retrieved from the range of "A1:E3" of all sheets except for "Sheet2" and "Sheet3".
function SAMPLE(range, exclude) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const excludeSheets = [ss.getActiveSheet().getSheetName(), ...exclude.split(",").map(e => e.trim())];
return ss.getSheets().flatMap(s => {
if (!excludeSheets.includes(s.getSheetName())) {
return s.getRange(range).getValues();
}
return [];
});
}
As another pattern, if you want to retrieve the values from all sheets in a source Spreadsheet to a sheet of destination Spreadsheet, how about the following sample script? In this sample, please copy and paste the following script to the script editor of the destination Spreadsheet. And, please set the variables of source Spreadsheet ID and range you want. When this script is run, a formula is put to the cell "A1" of the destination sheet. The formula is from this thread. By this, you can see the values from all sheets in the source Spreadsheet at the destination sheet.
function myFunction2() {
const range = "A1:E3"; // Please set the range you want to retrieve.
const srcSpreadsheetId = "###"; // Please set source Spreadsheet ID.
const srcSs = SpreadsheetApp.openById(srcSpreadsheetId);
const url = srcSs.getUrl();
const values = srcSs.getSheets().map(s => `IMPORTRANGE("${url}","'${s.getSheetName()}'!${range}")`);
const formula = `={${values.join(";")}}`;
const dstSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1"); // Please set the destination sheet name.
dstSheet.clearContents().getRange(1, 1).setFormula(formula);
}
References:
getSheets()
getValues()
setValues(values)
Added:
About your following reply,
I'm using the first block of code that you sent, and it works. However, I want to copy the entire cell so that it also transfers the cell color, in addition to the text content of the cell. How would I modify the first block of code you sent to do that?
Unfortunately, from your question, I couldn't notice that you wanted to copy both the values and the background color of the cells. To achieve this request, the modified script of my 1st script is as follows.
Sample script:
function myFunction() {
const range = "A1:E3"; // Please set the range you want to retrieve.
const srcSpreadsheetId = "###"; // Please set source Spreadsheet ID.
const dstSpreadsheetId = "###"; // Please set destination Spreadsheet ID.
const dstSheetName = "Sheet1"; // Please set destination sheet name.
const { values, backgroundColors } = SpreadsheetApp.openById(srcSpreadsheetId).getSheets().reduce((o, s) => {
const r = s.getRange(range);
o.values = [...o.values, ...r.getValues()];
o.backgroundColors = [...o.backgroundColors, ...r.getBackgrounds()];
return o;
}, { values: [], backgroundColors: [] });
const dstSheet = SpreadsheetApp.openById(dstSpreadsheetId).getSheetByName(dstSheetName);
dstSheet.clearContents().clearFormats().getRange(1, 1, values.length, values[0].length).setValues(values).setBackgrounds(backgroundColors);
}

Google Sheet App Scripts : How to parse JSONs in Sheet A column A to Sheet B?

I have a google spreadsheet with Sheet A having JSONs in column A and I'd like to populate Sheet B with the data with respective rows and columns. However I'm not entirely sure how to start. I'm a bit confused about the whole process. First declaring sheet. I've seen people using the following:
function parseJSON()
{
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Gets the Active Spreadsheet
var sheet = ss.getSheets()[0]; // Gets the first sheet
var parsed = JSON.parse(json);
sheet.appendRow([json.items[i].id, json.items[i].name, json.items[i].url]);
}
but I have in 1 active spreadsheet from sheet A to sheet B. So would i need to declare a second sheet (my sheet B)?
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Gets the Active Spreadsheet
var sheet1 = ss.getSheets()[0]; // Gets the first sheet
var sheet2 = ss.getSheets()[1]; // Gets the first sheet
sheet2.appendRow([json.items[i].id, json.items[i].name, json.items[i].url]);
or i dont need those declarations and i just
function parseJSON(parsed)
{
return [parsed.items[i].id, parsed.items[i].name, parsed.items[i].url]
}
so in Sheet B A1 i just put =parseJSON(SheetB!A) or something equivalent which im still unsure.
Also how do i declare what are headers and the values according to the JSON? Should the headers be declared?
const headers = ["dish_name","dish_price","dish_quantity"];
or is it possible to get the header from the JSON? How about the corresponding values? Does Apps Ssripts accept foreach loop?
For additional context (for selected answer that helped)
JSON Obj - {"55":{"dish_name":"(Ala Carte) Claypot Soup with Rice and Char Kuih","dish_price":17,"dish_quantity":1,"dish_size_name":"default","dish_size_price":0,"dish_addon_name":"default","dish_addon_price":0,"dish_variation_name":"default","dish_variation_price":0}}
the selected answer was able to split it up in key value pairs. so constant keys were headers, rows were the corresponding values.
In your situation, how about the following modification?
Modification points:
About your following script,
function parseJSON()
{
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Gets the Active Spreadsheet
var sheet = ss.getSheets()[0]; // Gets the first sheet
var parsed = JSON.parse(json);
sheet.appendRow([json.items[i].id, json.items[i].name, json.items[i].url]);
}
In this case, sheet, json are not declared. By this, an error occurs.
When sheet, json are declared, parsed is not used.
Answer for question 1:
but I have in 1 active spreadsheet from sheet A to sheet B. So would i need to declare a second sheet (my sheet B)?
I think that it's yes. I think that when you want to retrieve the values from the column "A" of Sheet "A" and the values are put to Sheet "B", it is required to declare both sheets.
Answer for question 2:
Also how do i declare what are headers and the values according to the JSON? Should the headers be declared? or is it possible to get the header from the JSON? How about the corresponding values? Does Apps Ssripts accept foreach loop?
I think that it's yes. When I saw your JSON data, it seems that several properties are existing. And, in your output situation, it is required to put the values in order. In this case, when the header value is used, the script can be easily prepared.
Sample script:
In order to achieve your goal, how about the following sample script?
function myFunction() {
// 1. Retrieve source and destination sheets.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const [srcSheet, dstSheet] = ["Sheet1", "Sheet2"].map(s => ss.getSheetByName(s));
// 2. Retrieve values from source sheet.
const values = srcSheet.getRange("A1:A" + srcSheet.getLastRow()).getValues();
// 3. Create an array for putting to the destination sheet using the header value.
const headers = ["dish_name","dish_price","dish_quantity"];
const ar = [["", ...headers], ...values.flatMap(([a]) => Object.entries(JSON.parse(a)).map(([k, v]) => [k, ...headers.map(h => v[h] || "")]))];
// 4. Put the array to the destination sheet.
dstSheet.getRange(1, 1, ar.length, ar[0].length).setValues(ar);
}
This sample script retrieves the values from column "A" of the source sheet "Sheet1" and converted the values to the 2-dimensional array and put the array to the destination sheet "Sheet2".
Please modify the sheet names of the source "Sheet1" and destination "Sheet2" sheets for your actual situation, respectively.
References:
map()
getValues()
setValues(values)

GoogleSheets ApsScript ReferenceError: ADDRESS is not definedDetails

In the following code snippet there is a very simple code.
When I try to use it I'm getting
ReferenceError: ADDRESS is not defined error.
I'll appreciate very much if someone could shed a light on this issue.
function temp() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange(ADDRESS(15,D1)).activate;
};
Issue 1:
ADDRESS is a google sheets function but you are trying to use it in Google Apps Script.
The latter does not accept google sheets formulas. It has its own documentation and only JavaScript is supported.
Issue 2:
To execute a function in any programming language you need to add parentheses at the end of the function. Replace activate with activate().
Solution:
You most likely want to take the range of cell D15. If that's the case then simply do:
function temp() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('D15').activate();
};
Keep in mind this will work for the active sheet only (the sheet that is currently selected). If you want to select a specific sheet by its name, then do that and change Sheet1 to the sheet name of your choice:
function temp() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName('Sheet1') // put the sheet name of your choice
sheet.getRange('D15').activate();
};
Updated answer based on your comment:
Assuming cell D15 contains a cell reference of the cell you want to activate, then do that:
function temp() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName('Sheet1') // put the sheet name of your choice
sheet.getRange(sheet.getRange('D15').getValue()).activate();
};
or based on your original code:
function temp() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange(spreadsheet.getRange('D15').getValue()).activate();
};

apps script getValue contents only

I have the following that works fine:
function phototable() {
var ss = SpreadsheetApp.openById('####');
var lastRow = ss.getLastRow();
ss.getRange('H'+lastRow)
.setValue('=VLOOKUP("Form Responses_Images/"&B'+lastRow+',importrange("https://docs.google.com/spreadsheets/d/####/edit","Form Responses!U:Z"),4,false)');
}
However, I don't want the formula copied into column H, just the actual value. So I tried:
ss.getRange('H'+lastRow)
.getValue('=VLOOKUP("Form Responses_Images/"&B'+lastRow+',importrange("https://docs.google.com/spreadsheets/d/###/edit","Form Responses!U:Z"),4,false)')
.copyTo('H'+lastRow,{contentsOnly:true});
But that doesn't insert anything into column H. Any ideas?
Modification points:
About I have the following that works fine:, when I saw the above script, I think that an error occurs at getValue. Because getValue has no arguments. I thought that getValue was setValue.
At copyTo('H'+lastRow,{contentsOnly:true}), the 1st argument is thr range object.
In order to copy the result of the formula during the script as the string, it is required to use flush.
When above points are reflected to your script, it becomes as follows.
Modified script:
function phototable() {
var ss = SpreadsheetApp.openById('####');
var lastRow = ss.getLastRow();
var range = ss.getRange('H'+lastRow);
range.setValue('=VLOOKUP("Form Responses_Images/"&B'+lastRow+',importrange("https://docs.google.com/spreadsheets/d/###/edit","Form Responses!U:Z"),4,false)');
SpreadsheetApp.flush();
range.copyTo(range, {contentsOnly:true});
}
Note:
In your script, ss is the 1st tab of the Spreadsheet. Please be careful this.
References:
flush()
copyTo(destination, options)
Issues / Explanation:
You have 2 ways to update the cell in column H with the value of the formula:
If you want to update the cell in H with its value then get the value and set it back:
range.setValue(range.getValue());
Use the copyTo() method:
range.copyTo(range, {contentsOnly:true});
As a bonus information, copyTo() can not be used if the source range and the target range are not of the same spreadsheet file.
Since this process happens really fast, it is a good idea to use flush() for the pending sheet changes to be completed.
Solution:
function phototable() {
var ss = SpreadsheetApp.openById('####').getSheetByName('Sheet1');
var lastRow = ss.getLastRow();
var range = ss.getRange('H'+lastRow);
range.setValue('=VLOOKUP("Form Responses_Images/"&B'+lastRow+',importrange("https://docs.google.com/spreadsheets/d/####/edit","Form Responses!U:Z"),4,false)');
SpreadsheetApp.flush();
range.setValue(range.getValue());
// range.copyTo(range, {contentsOnly:true}); // choose this or the previous line
}
Modify Sheet1 to the name of the sheet you want to apply this operation.
Also it is a better practice to choose a particular sheet before you apply any function to it. To get a sheet by its name use getSheetByName().