Clone Googlsheet which has formulas/importranges - javascript

I would like to clone/copy a google sheet and locate it in a drive, The script i have at the moment is working for this element but the contents appear blank becuase of formulas/importranges
Please can you help with this. Current script is
function cloneGoogleSheet2() {
const destFolder = DriveApp.getFolderById("xxxxxxxxxxx");
const file = DriveApp.getFileById("xxxxxxxxxxxxxxx").makeCopy("xxxxxxxxx", destFolder);
const ss = SpreadsheetApp.openById(file.getId());
const sheets = ss.getSheets();
sheets.forEach(sh=>{
let rng = sh.getDataRange();
rng.copyTo(rng, {contentsOnly:true});
SpreadsheetApp.flush();
});
}
Reference to similar question below
Copy a spreadsheet file to Google Drive and replace all the formulas with values

Explanation:
Unfortunately:
importranges can not be allowed programmatically. So you need to set get the values from the source spreadsheet and paste them to the newly created (target) spreadsheet.
copyTo can not be used between two different spreadsheets, so you can use getValues and setValues instead.
The logic is to iterate over the source sheets and for every sheet get values and copy them to the corresponding target sheets.
Solution:
function cloneGoogleSheet2() {
const destFolder = DriveApp.getFolderById("folder_id");
const source_id = "spreadsheet_id_to_copy";
const source_ss = SpreadsheetApp.openById(source_id);
const file = DriveApp.getFileById(source_id).makeCopy("new_file", destFolder);
const target_ss = SpreadsheetApp.openById(file.getId());
const source_sheets = source_ss.getSheets();
const target_sheets = target_ss.getSheets();
source_sheets.forEach((sh,i)=>{
let values = sh.getDataRange().getValues();
target_sheets[i].getDataRange().setValues(values);
SpreadsheetApp.flush();
})
}

Related

Adding tab name in column while importing from Sheet to Sheet in Apps Script

I've struggled with this for a while & have put it off because I've always been able to just edit the SOURCE spreadsheet and add an IF statement with the TABNAME.
Meaning, in an adjacent column, I'll write, if Cell = blank, do nothing, if not give me tab name.. then I'll simply include that column in my import.
After putting off the true issue for a while, I'm unable to edit the source sheet in this instance -- causing me to go down a rabbit hole of trying to alter this script.
Here's what I have:
A script that imports sheet to sheet (prefer this over importrange):
function scheduleimport() {
//#NotOnlyCurrentDoc
var values = SpreadsheetApp.openById('spreadsheetID').
getSheetByName('SourceSheet').getRange('A1:N').getValues();
SpreadsheetApp.getActive().getSheetByName('DestinationSheet').
getRange(1,1,values.length,values[0].length).setValues(values);}
This works super well & I've lived off it for a while...
What I'm looking to accomplish is putting the TABNAME of the source sheet in the column right next to the import range (only in cells where the range has values - not the entire destination sheet).
I came across this script that uses the length of a specific column & places a value wherever designated. I tweaked it to include the TABNAME:
function fillColB() {
var s = SpreadsheetApp.getActiveSheet();
tabname = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
var data = s.getRange("a1:a").getValues();
var data_len = data.length;
for(var i=0; i<data_len; i++) {
if(data[i][0].length == 0) {
s.getRange(i+1,2).setValue("");
} else {
s.getRange(i+1,2).setValue(tabname);
}
}
}
I tried tying these together - essentially, getting the length of the import, and adding in the TABNAME as a value next to the last column. I am struggling heavy with it though -- any guidance would be much appreciated.
How about this:
function scheduleimport() {
const ss = SpreadsheetApp.openById('spreadsheetID');
const sh1 = ss.getSheetByName("SourceSheet");
const vs = sh1.getRange('A1:N' + sh1.getLastRow()).getValues();
const sh2 = ss.getSheetByName('DestinationSheet');
sh2.getRange(1, 1, vs.length, vs[0].length).setValues(vs);
sh2.getRange(1,sh2.getLastColumn() +1).setValue(sh1.getName());
}
I may have read you code incorrectly but for an import I would think that you might wish the source spreadsheet and the destination spreadsheet to be different. So this next one provides that functionality
function scheduleimport() {
const ss = SpreadsheetApp.openById(gobj.globals.testsourceid);//ids are coming from global object in the active sheet
const dss = SpreadsheetApp.getActive();
const sh1 = ss.getSheetByName("Sheet0");
const vs = sh1.getRange('A1:N' + sh1.getLastRow()).getValues();
const sh2 = dss.getSheetByName('Sheet1');
sh2.getRange(1, 1, vs.length, vs[0].length).setValues(vs);
sh2.getRange(1,sh2.getLastColumn() +1).setValue(sh1.getName());
}
Putting the sheet name into a newlast column on all rows of the transferred data set.
function scheduleimport() {
const ss = SpreadsheetApp.openById(gobj.globals.testsourceid);//ids are coming from global object in the active sheet
const dss = SpreadsheetApp.getActive();
const sh1 = ss.getSheetByName("Sheet0");
const vs = sh1.getRange('A1:N' + sh1.getLastRow()).getValues();
const sh2 = dss.getSheetByName('Sheet1');
sh2.getRange(1, 1, vs.length, vs[0].length).setValues(vs);
sh2.getRange(1,sh2.getLastColumn() +1,vs.length).setValue(sh1.getName());
}

Can I use Google Apps Script to replace text across multiple tabs in a Google Sheet?

I am building on a previous project in which I have a Google Form which takes responses in a Google Sheet and uses a template Sheet to populate the Form responses and have that generate a new Sheet document. This is a very dumbed-down version of what I'm trying to execute in reality, but the goals remain the same: I am trying to replace text across multiple tabs in the template Sheet when generating a new one.
Currently, in my Apps Script, I have code which is successfully able to make a copy of the template file and name it accordingly:
//Enter collected info into Requirements Template
const googleSheetTemplate = DriveApp.getFileById('1wqCwMhpuDLReU1hE1CbcDL-Vdw_4zge1xM6oOl34Ohg');
const destinationFolder = DriveApp.getFolderById('1GxNZQmP8mxHBhVl5AMoqBFs8sAIYzcm3');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form Responses 2');
const copy = googleSheetTemplate.makeCopy(`${row[3]}, ${row[0]} Vehicle Order` , destinationFolder);
const newSheet = SpreadsheetApp.openById(copy.getId());
const A1 = newSheet.getDataRange();
And the next few lines which are meant to be able to find and replace certain strings within the newly copied Sheet are as follows:
A1.createTextFinder("{{Customer}}").replaceAllWith(row[3]);
A1.createTextFinder("{{Car}}").replaceAllWith(row[1]);
A1.createTextFinder("{{Color}}").replaceAllWith(row[2]);
A1.createTextFinder("{{Delivery}}").replaceAllWith(row[5]);
The issue I am experiencing is that the first tab of the Sheet gets populated, but the second tab does not.
Is there more I must add somewhere in order to get the second tab filled out? Is this even possible in Google Apps Script?
Entire code below:
function myFunction() {
// get the spreadsheet information
const ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//const responseSheet = ss.getSheetByName('Form Responses 2');
const data = ss.getDataRange().getValues();
//console.log(data);
// Loop over the rows
data.forEach((row,i) => {
// Identify whether notification has been sent
if (row[4] === '') {
// Get the Form info
var emailTo = "jeffreyabr#gmail.com"
var subject = 'Car Request';
const Timestamp = row[0];
var Car = row[1];
var Color = row[2];
var requestor = row[3]
var delivery = row[5];
//Form variable declarations
formTime = Timestamp;
formCar = Car;
formColor = Color;
formName = requestor;
formDelivery = delivery;
//Enter collected info into Requirements Template
const googleSheetTemplate = DriveApp.getFileById('1wqCwMhpuDLReU1hE1CbcDL-Vdw_4zge1xM6oOl34Ohg');
const destinationFolder = DriveApp.getFolderById('1GxNZQmP8mxHBhVl5AMoqBFs8sAIYzcm3');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form Responses 2');
const copy = googleSheetTemplate.makeCopy(`${row[3]}, ${row[0]} Vehicle Order` , destinationFolder);
const newSheet = SpreadsheetApp.openById(copy.getId());
const A1 = newSheet.getDataRange();
A1.createTextFinder("{{Customer}}").replaceAllWith(row[3]);
A1.createTextFinder("{{Car}}").replaceAllWith(row[1]);
A1.createTextFinder("{{Color}}").replaceAllWith(row[2]);
A1.createTextFinder("{{Delivery}}").replaceAllWith(row[5]);
const orderLink = newSheet.getUrl();
//Add URL to Sheet
sheet.getRange(i + 1, 7).setValue(orderLink)
orderBlob = [];
//Get the blob of order attachment
if(row[6]){
var order1 = row[6].split(', ');
order1.forEach(url => {
var orderFileId = url.replace('https://drive.google.com/open?id=','');
var orderFile = DriveApp.getFileById(orderFileId);
orderBlob.push(orderFile.getBlob());
});
}
let body = '';
// Generate email
var html = HtmlService.createTemplateFromFile("email.html");
var htmlText = html.evaluate().getContent();
// Send email
GmailApp.sendEmail(emailTo, subject, body, {htmlBody: htmlText, attachments: orderBlob})
// Mark as Notified
const g = 'Notification sent';
ss.getRange(i + 1,5).setValue(g);
}
})
}
Answer
You can try a looping method to access all of the sheet tabs inside of your newSheet Spreadsheet file using the getSheets() method.
Just replace part of your script from the creation of newSheet to the last line of A1.createTextFinder with the script below:
[UPDATED]
Sample Script
const newSheet = SpreadsheetApp.openById(copy.getId());
for (currentSheet = 0; currentSheet < newSheet.getSheets().length; currentSheet++) {
const a1 = newSheet.getSheets()[currentSheet].getDataRange();
a1.createTextFinder("{{Customer}}").replaceAllWith(row[3]);
a1.createTextFinder("{{Car}}").replaceAllWith(row[1]);
a1.createTextFinder("{{Color}}").replaceAllWith(row[2]);
a1.createTextFinder("{{Delivery}}").replaceAllWith(row[5]);
}
Sample Test:
Sample copy of the newSheet file w/ 2 sheet tabs
Script test demonstration
The newSheet file that contains 2 sheet tabs:
Sheet 1
Sheet 2

Google Sheets Apps Script Matching Source Destination Sheets having same name tabs

I've a need to copy values between tabs of source & destination sheets. The tabs are same name H1, H2, H3 in both sheets. Copying is between tabs of same name i.e., H1->H1, H2->H2, etc. The script is attached below. Every time I've to run the script, I'm changing the sheet name manually. How to do it in one go?
function updateSourceToTarget(sourceID,sourceName,targetID,targetname){
var source = SpreadsheetApp.openById("Source").getSheetByName("H1");
var destination = SpreadsheetApp.openById("Dest").getSheetByName("H1");
var sourcelastRow = source.getLastRow();
var sourcelastCol = source.getLastColumn();
var sourcedata = source.getRange(1,1,sourcelastRow,sourcelastCol).getValues();
destination.getRange(1,1,sourcelastRow,sourcelastCol).setValues(sourcedata);
}
Explanation:
Iterate over an array of the sheet names ["H1","H2","H3"] with a forEach loop.
Put all the sheet names in that array, and the script will take care of the task for every set of source and destination sheet.
Solution:
function updateSourceToTarget(sourceID,sourceName,targetID,targetname){
const sheetNames = ["H1","H2","H3"];
sheetNames.forEach(h=>{
let source = SpreadsheetApp.openById("Source").getSheetByName(h);
let destination = SpreadsheetApp.openById("Dest").getSheetByName(h);
let sourcelastRow = source.getLastRow();
let sourcelastCol = source.getLastColumn();
let sourcedata = source.getRange(1,1,sourcelastRow,sourcelastCol).getValues();
destination.getRange(1,1,sourcelastRow,sourcelastCol).setValues(sourcedata);
});
}
and I guess this is the final version that uses the function parameters:
function updateSourceToTarget(sourceID,targetID){
const sheetNames = ["H1","H2","H3"];
sheetNames.forEach(h=>{
let source = SpreadsheetApp.openById(sourceID).getSheetByName(h);
let destination = SpreadsheetApp.openById(targetID).getSheetByName(h);
let sourcelastRow = source.getLastRow();
let sourcelastCol = source.getLastColumn();
let sourcedata = source.getRange(1,1,sourcelastRow,sourcelastCol).getValues();
destination.getRange(1,1,sourcelastRow,sourcelastCol).setValues(sourcedata);
});
}

How can i add list of sheets to Google App Script?

I have a script, which works in a tab, which is hardcoded in the line 4 (Sheet1):
function fillColumn() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet1');
const values = sh.getRange('A2:A'+sh.getLastRow()).getValues().flat([1]);
var toCopy;
values.forEach((el,index)=>{
if (el != ''){
toCopy = sh.getRange(index+2,1).getValue();
} else {
sh.getRange(index+2,1).setValue(toCopy)
}
})
}
What is the correct way to add a list of tabs, to make script working in tabs Sheet1, Sheet2, Sheet12? I tried some notations, but they were all erroneous.
Explanation:
You need to loop over the sheets. There are multiple ways to do that, but since you are already familiar with the forEach loop, you can wrap your code with another forEach loop that iterates over the selected sheets.
You want to iteratively get and set values in multiple sheets, then it is a good idea to flush the changes at the end of each sheet iteration.
Solution:
function fillColumn() {
const ss = SpreadsheetApp.getActive();
["Sheet1", "Sheet2", "Sheet12"]. // Add sheets here
forEach(sn=>{
let sh = ss.getSheetByName(sn);
let values = sh.getRange('A2:A'+sh.getLastRow()).getValues().flat();
let toCopy;
values.forEach((el,index)=>{
if (el != ''){
toCopy = sh.getRange(index+2,1).getValue();
} else {
sh.getRange(index+2,1).setValue(toCopy)
}
SpreadsheetApp.flush();
});
});
}
You can simply iterate through ss.getSheets which returns an array of the sheets in the document:
for (let sheet of ss.getSheets()) {
// Do something for each sheet
}
If you don’t want to iterate all of them, you can have an array of the names, iterate it, and use the name to get the sheet:
const sheetNames = [
'Sheet 1',
'Special sheet',
'Sheet 25',
]
for (let name of sheetNames) {
const sheet = ss.getSheetByName(name)
// Do something with sheet
}
References
Spreadsheet getSheets()
Spreadsheet getSheetByName(name)

Google Sheets save the style of sheet while merging it to another sheet

I need to save the styles of the sheets when I combine them into a mainsheet. Here is the code:
function myFunction()
{
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSheet = activeSpreadsheet.getSheetByName("MainSheet");
if (newSheet != null) {
activeSpreadsheet.deleteSheet(newSheet);
}
newSheet = activeSpreadsheet.insertSheet();
newSheet.setName("MainSheet");
const ss = SpreadsheetApp.getActiveSpreadsheet();
const allSheets = ss.getSheets();
const allSheets_names=allSheets.map(sheet=>sheet.getSheetName())
const dataRange = "A1:M";
const checkRange = "A1:A";
const neededSheets = ["Cats", "Dogs"];
const filteredListOfSheetsNames = [];
neededSheets.forEach(function(ns){
var i = neededSheets.indexOf(ns);
filteredListOfSheetsNames[i]=[];
allSheets_names.forEach( (as,index) => {
if (as.indexOf(ns)>-1){
filteredListOfSheetsNames[i].push(as);
}
}
)
const filteredListOfSheets = filteredListOfSheetsNames[i].map(name =>ss.getSheetByName(name));
var array = [];
filteredListOfSheets.forEach(function(sheet){var values = sheet.getRange(1,1,sheet.getLastRow(),13).getValues(); array.push(values);});
array = [].concat.apply([],array);
if(array.length > 0){
newSheet.getRange(1,i*13+1, array.length, array[0].length).setValues(array);
}
}
)
}
Currently, I get all the information correctly. On one side of the mainsheet I get sheets that contain a word "dog" in them and on another side of the mainsheet I get the sheets that contain a word "cat". The issue is that the font, colors disappear after I combine all the sheets. How should I keep the styles when combining the sheets?
Here is an example of the MainSheet on how it should look like, but it should also take all of the styles from the sheets.
You can use copyTo to copy the formatting from one sheet to another. See documentation here.
Here is a minimal example based off of your script that copies the formatting to mainSheet from sheets Cats and Dogs. This script assumes that the script will be attached to the spreadsheet on which it is to operate, which you're likely already doing.
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const mainSheet = ss.getSheetByName("MainSheet");
const neededSheets = ["Cats", "Dogs"];
mainSheet.clearFormats().clear() // clear formatting and values on main sheet
for(var i=0; i<neededSheets.length; i++)
{
var sheet = ss.getSheetByName(neededSheets[i]) ;
var values = sheet.getRange(1,1,sheet.getLastRow(),13).getValues();
mainSheet.getRange(1,i*13+1, values.length, values[0].length).setValues(values);
var sourceRange = sheet.getRange(1,1,sheet.getLastRow(),13) // define the range that has the formatting you want to copy
var targetRange = mainSheet.getRange(1,i*13+1, values.length, values[0].length) // define the range that you want to copy the formatting to
sourceRange.copyTo(targetRange, {formatOnly:true}) // copy the format
}
}

Categories

Resources