Why can't I copy ranges between spreadsheets with copyValuesToRange()? - google-apps-script

I am able to copy a range of values from one spreadsheet to another with Range.setValues(values) but not with Range.copyValuesToRange(sheet, column, columnEnd, row, rowEnd):
function test()
{
var sourceSheet = SpreadsheetApp.getActiveSheet();
var sourceCols = sourceSheet.getRange("A1:B10");
// Copying the first two columns this way works.
var sheet1 = SpreadsheetApp.create("New Spreadsheet 1").getActiveSheet();
sheet1.getRange("A1:B10").setValues(sourceCols.getValues());
// Copying the first two columns this way does not work.
var sheet2 = SpreadsheetApp.create("New Spreadsheet 2").getActiveSheet();
sourceCols.copyValuesToRange(sheet2, 1, 2, 1, 10);
}
Specifically, after running the code, New Spreadsheet 1 contains range A1:B10 of the source spreadsheet, while New Spreadsheet 2 is empty. Both have a single sheet named "Sheet1". The source spreadsheet is unmodified.
What am I doing wrong? I'm wondering if the failing method only works within a single spreadsheet, but I see nothing in the documentation about that.
The reason I want to use Range.copyValuesToRange() is so I can also copy the formatting with Range.copyFormatToRange().

The methods copyValuesToRange and copyFormatToRange, as well as Range.copyTo, are restricted to copying within the same spreadsheets, although documentation is not explicit about that. A workaround which allows you to copy between spreadsheets with formatting is to copy the entire sheet to the destination spreadsheet, using Sheet.copyTo method. Then copy the required range from there, and optionally, delete the sheet. Like this:
function cc() {
var ss1 = SpreadsheetApp.openByUrl('some url');
var ss2 = SpreadsheetApp.openByUrl('another url');
var sheet1 = ss1.getSheetByName('name');
var sheet2 = ss2.getSheetByName('another name');
// the above was the setup, main code begins here
var copySheet = sheet1.copyTo(ss2);
copySheet.getRange("A1:B2").copyTo(sh2.getRange("C1:D2"));
ss2.deleteSheet(copySheet);
}

Related

Trigger when change is happening in specific sheet

I have a sheet with data that is imported through IMPORTRANGE to another sheet. When I make changes to Spreadsheet 1 >>> Spreadsheet 2 I have a script that copy the data from Copy to Paste. It is working all fine however I want to make sure that the script only runs when changes are made in the 'Copy' sheet and not any other. At the moment it runs independent on what sheet I make changes in.
Spreadsheet 1
Spreadsheet2
I tried onChange trigger two different ways...
This one makes the change but triggers when changes are made in any of the sheets
function Trigger2(e) {
var sheet = e.source.getActiveSheet();
if (sheet.getName() === 'Copy') {
copyInfo();
}
}
AND
(this does not work)
function Trigger2(e) {
var sheet = e.source.getActiveSheet();
var sheet = SpreadsheetApp.getActiveSpreadsheet();
if( sheet.getActiveSheet().getName() !== "Copy" ) return;{
copyInfo();
}}
The copy code looks like this...
function copyInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
// get source range
var source = copySheet.getRange(2,1,12,8);
// get destination range
var destination = pasteSheet.getRange(2,1,12,8);
// copy values to destination range
source.copyTo(destination);
}
Copy Changes from one Sheet To Another after Imported Range Changes
Spreadsheet 2 Imports a range to Sheet1 from Spreadsheet 1 Sheet1 and when a change occurs in Spreadsheet 2 Sheet1 we copy data from Spreadsheet2 Sheet1 to Spreadsheet2 Sheet 2.
function onMyChange(e) {
//Logger.log(JSON.stringify(e));//useful during setup
//e.source.toast("Entry");//useful during setup
const sh = e.source.getSheetByName("Sheet1")
Logger.log(sh.getName());
if(e.changeType == "OTHER") {
let tsh = e.source.getSheetByName("Sheet2");
sh.getRange(2,1,12,8).copyTo(tsh.getRange(2,1));//Only need the upper left hand corner of the range. Thus you change change the size of the source data without having to change the target range.
}
}
One might be inclined to attempt to use e.source.getActiveSheet() unfortunately this does not necessary get the sheet that you have written unless in is SpreadsheetApp.getActive().getSheets()[0]. onChange event object always returns the most left sheet in the spreadsheet in this situation so it can't be used to identify the sheet that is being written to from the importRange function.
With this function you no longer require another function.
A simple if should work, but you're trying to compare the name to the wrong field. According to Google's documentation, the source field in the Event Object e returns the Spreadsheet object, which is the entire document.
Instead you can use the e.range field, which contains the exact range where the edit was made, and the Range class has a getSheet() method to get the parent Sheet.
So you can modify your sample to the following:
function Trigger2(e) {
var sheet = e.range.getSheet();
if (sheet.getName() === 'Copy') {//Edit: this will only work if sheet is the most left sheet in the spreadsheet
copyInfo();
}
}

Is possible to copy a data range from one spreadsheet to another?

function transferDataToDataBase(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var DB = SpreadsheetApp.openById("SHEET ID");
var SCPM_Input = ss.getSheetByName("Report")[0];
var history_Input = DB.getSheetByName(Database);
var blankRow = history_Input.getLastRow();
SCPM_Input.getRange(("A2:T").copyTo(history_Input.getRange(blankRow+1,1,blankRow+1,20));
}
Question:
The last line has an error at ";", don't know how to fix it. It said the syntax error;
It seems copy To only works for transferring from one sheet to another in the same spreadsheet, but not in between spread sheet. Is there a way to copyTo between spreadsheets?
Many thanks.
If you want to copy the formatting as well then copy the entire sheet/tab and then use copyTo() to copy the range and then delete the copied sheet. Or if you just want the data use getValues() and setValues();
This is part of the problem:
var SCPM_Input = ss.getSheetByName("Report")[0]; because the index zero at the end should not be there and you cannot have sheet.getLastRow() + 1 number of rows in a sheet as shown in the next row.
SCPM_Input.getRange(("A2:T").copyTo(history_Input.getRange(blankRow+1,1,blankRow+1,20));
And copyTo doesn't work between spreadsheets.
There is an update:
Since the database will be used by multiple users. I set up a standalone database. I copied my "Report" sheet from spreadsheet 1 to spreadsheet 2. It will create a sheet "copy of Report".I rename, copied to Database sheet and delete it.
Feel free to discuss if there is an easier way to do it. Thanks.
// transfer the report to DataBase Spreadsheet
function transferDataToDataBase(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var db = SpreadsheetApp.openById('spreadsheet ID')
var scpm_Input = ss.getSheetByName("Report");
scpm_Input.copyTo(db);
Utilities.sleep(3000);
db.getSheetByName('copy of report').setName('Import')
var history_Input = db.getSheetByName('Database');
var blankRow = history_Input.getLastRow();
var target = db.getSheetByName('Import');
target.getRange("A2:T").copyTo(history_Input.getRange(blankRow+1,1,blankRow+1,20));
history_Input.getRange(blankRow+1,21).setValue(new Date()).setNumberFormat("yyyy-mm-dd h:mm") //update the date and time
db.deleteSheet(target);
}

Trying to build G Sheets scrip that will copy and paste a dynamic range within same spreadsheet

I'm trying to build a script that will allow me to copy and value paste a range from tab "robert" to tab "brendan". However, the range in "robert" is dynamic, so sometimes it will need to copy and paste A1:M3 or A1:M20. I'd also like it to append to the the first rows of the destination. So when run, the script looks for values on the source tab (robert), copies them, and pastes their values at the top of the destination (brendan) without overwriting existing data.
So far I've worked with the below script from a different thread:
function moveValuesOnly() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getRange('Robert!A1:M100');
source.copyTo(ss.getRange('Brendan!A4'), {contentsOnly: true});
source.clear();
}
The problem here is the script range is static, and it overwrites anything in the destination. I do like the clearing of the source though and the values paste. Any help would be greatly appreciated!
Define the range using variables. Use getLastRow() and getLastColumn() Instead of hard-coding it.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var robWs = ss.getSheetByName("Robert");
var brenWs = ss.getSheetByName("Brendan");
var robLr = robWs.getLastRow()
var robLc = robWs.getLastColumn()
//range starts at row 1 and column 1 (A1), you may need to change accordingly
var vals = robWs.getRange(1,1,lr,lc).getValues;
//paste range also starts at A1. you can find the lr on this sheet and start the range there
brenWs.getRange(1,1,lr,lc).setValues(vals);
function moveValuesOnly() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Robert');
var rg=sh.getActiveRange();//copies active range for source
var vA=rg.getValues();
dsh=ss.getSheetByName('Brendan');
dsr=4;//destination start row
dsc=1;//destination start col
dsa=2;//destination number of rows to append to
drg=dsh.getRange(dsr,dsc,rg.getHeight(),rg.getWidth());
var dvA=drg.getValues();
vA.forEach(function(r,i){
if(i<dsa){
r.forEach(function(c,j){
//only write data in destination for these rows if destination value is null
if(dvA[i][j]='') {
dvA[i][j]=c;
}
});
}else{
dvA[i][j]=c;
}
})
drg.setValues(dvA);
}

Google app script to cut and paste a range from a google sheet to another google sheet

I'm trying to put together a script that cuts a range from a google spreadsheet and pastes it to another spreadsheet. The script works fine as long as the source sheet is the first sheet in my document. How can I fix it so that the script grabs the right sheet no matter where it is in my document?
I'm very very new at this and mostly used code I found online. Probably made a very obvious mistake..
function CopyRangeTo_OtherSheet() {
var sss = SpreadsheetApp.openById('1O1gML-IeOkx6n6RhCavay6Zg9zflalcVs0qkB2yu7bE');
var ss = sss.getSheetByName('Sheet1');
var range = ss.getRange(2,4,sss.getLastRow()-1,9);
var data = range.getValues();
var tss = SpreadsheetApp.openById('1WHoETM3QxV166ndnPW4Qv37Y6SBGPVw-CcEQxNpw0Co');
var ts = tss.getSheetByName('Sheet3');
ts.getRange(ts.getLastRow()+1, 1,50,9).setValues(data);
var spread = SpreadsheetApp.openById('1O1gML-IeOkx6n6RhCavay6Zg9zflalcVs0qkB2yu7bE');
var sheet = spread.getSheetByName("Sheet1");
sheet.getRange("D2:H").clearContent();
}
The number of rows will be different in each sheet. .getLastRow() on a spreadsheet class only returns the last row in first sheet. getLastRow of sheet instead
ss.getLastRow()
Your line var ss = sss.getSheetByName('Sheet1'); implies that you take the sheet with the name Sheet1.
This is the sheet you use to obtain your values from range and subsequently copy them.
If you want to copy / cut and paste values from a different sheet, simply paste the correct name of the sheet in var ss = sss.getSheetByName('PASTE HERE THE NAME OF THE SHEET');
Small annotation:
If the range you want to clear is the same from which you copied the
values, you do not need to define it again. You can simply replace the
lines
var spread = SpreadsheetApp.openById('1O1gML-IeOkx6n6RhCavay6Zg9zflalcVs0qkB2yu7bE');
var sheet = spread.getSheetByName("Sheet1");
sheet.getRange("D2:H").clearContent();
through
range.clearContents();
Also note:
setValues() only works if the range into which the values shall be set has the same dimension like the value range, i.e. the original range.
If your original range is 2,4,ss.getLastRow()-1,9, then the number of rows is ss.getLastRow()-1 and the number of columns 9.
Thus, you need to define
ts.getRange(ts.getLastRow()+1, 1,ss.getLastRow()-1,9).setValues(data);
to adapt the row number dynamically.

Selecting specific range instead of active range in App Script

I have written a script on Google App Script that will, at one point, get a range from one sheet, copy the values on that range and paste them to another sheet.
I have done so by doing this:
var range = sh.getDataRange().offset(0, 0);
var data = range.getValues();
var ts = SpreadsheetApp.openById('SHEETID').getSheetByName('SHEETNAME')
ts.getRange(ts.getLastRow() + 1, 1, data.length, data[0].length).setValues(data)
I would like, however, to specify the exact range to copy and another exact range to paste the data into.
I would like the range to be copied/pasted to be from columns A to Z whilst still keeping the getLastRow() in the script.
How would I do that?
Thanks for your help.
You can use getRange(a1Notation) to fetch a range and its contents. I usually go for A1 notation. Sample:
// Get a range A1:D4 on sheet titled "Invoices"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange("Invoices!A1:D4");
// Get cell A1 on the first sheet
var sheet = ss.getSheets()[0];
var cell = sheet.getRange("A1");
There are other variations of getRange you can use. Choose what you're comfortable with.
For copying and pasting altogether, you can use the copyTo(destination)
// The code below copies the first 5 columns over to the 6th column.
var sheet = SpreadsheetApp.getActiveSheet();