I'm creating a script in Google Sheets that will copy the active sheet and create 30 duplicate sheets within the same workbook. Each duplicated sheet will have a different name based on the value within a cell on the active sheet. The cell will contain a date; duplicated sheets will have names of dates after the date listed in the cell. Example, cell B3 is "7/5/2019". The duplicated sheets should be named, "July 6, 2019" (B3+1), "July 7, 2019" (B3+2), & "July 8, 2019" (B3+3), etc.
I'm using code that is already embedded within Google Sheets. Some of it was created by recording a macro and other parts were created through what little I know about coding and research online.
function duplicatesheet(){
//copy active sheet
var as = SpreadsheetApp.getActiveSpreadsheet()
SpreadsheetApp.getActiveSpreadsheet().duplicateActiveSheet();
//rename sheet
var myValue =
SpreadsheetApp.getActiveSpreadsheet().getRange('B3').getValue();
SpreadsheetApp.getActiveSpreadsheet().renameActiveSheet(myValue);
}
The code works in duplicating the active sheet once, but it is not making 30 duplicates. It is also not renaming the sheets properly as described above based on the date listed in cell B3. I need help creating code that will accomplish both of those tasks.
This is edited w.r.t. comments. You can also see the excellent answer by Tedinoz.
Try this code:
function duplicatesheet() {
var as = SpreadsheetApp.getActiveSpreadsheet(); // active spreadsheet
var s = as.getActiveSheet(); // first sheet object
var dateCell = "B3"; // cell containing first date
var N = 30; // number of copies to make
var startDate = new Date(s.getRange(dateCell).getValue()); // get the date stored in dateCell
var day = startDate.getDate(); // extract the day
var month = startDate.getMonth(); // extract the month
var year = startDate.getFullYear(); // extract the year
// loop over N times
for (var i = 0; i < N; i++) {
var asn = s.copyTo(as); // make a duplicate of the first sheet
var thisSheetDate = new Date(year, month, day+(i+1)); // store the new date as a variable temporarily
asn.getRange(dateCell).setValue(thisSheetDate); // writes the date in cell "B3"
asn.setName(Utilities.formatDate(thisSheetDate, undefined, "MMMMM d, yyyy")); // sets the name of the new sheet
}
}
I suggest putting N=30 there to something small, like N=2 to see whether it works with your formatting first.
The formatting here used by the Utilities.formatDate() method, I have assumed it to be MMMMM d, yyyy, which will print tab names in this format:
July 6, 2019
You may change it as you wish according to the reference [3] below.
You can see the references for all the functions used here:
Sheet.copyTo() method
Sheet.getRange() method
Java's SimpleDateFormat, used by Utilities.formatDate()
This code takes the single date entered by the OP in Cell "B3" of sheet name, say, "Sheet1; it loops thirty times creating a duplicate of the initial spreadsheet, incrementing the data-based sheet name by 1 day each time.
To ensure accurate date math, it's suggested that the format of Cell "B3" should be in the same style ("MMMM dd, yyyy") as the proposed sheet names.
function so5691088602(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Sheet1";
var basesheet = ss.getSheetByName(sheetname);
var newSheetName = new Date(basesheet.getRange("B3").getValue());
var NumDups = 30;
for (var i=0;i<NumDups;i++){
basesheet.activate;
var tempDate = new Date();
var printdate01 = Utilities.formatDate(new Date(tempDate.setDate(newSheetName.getDate()+1+i)), "GMT+10", "MMMM dd, yyyy");
// Logger.log("DEBUG: Sheet Date = "+printdate01);
ss.insertSheet(printdate01, {template: basesheet});
}
}
Screenshot
Related
In Google Sheets, I have a tab titled "Scheduled" that has all our client's import dates. I need to automatically hide (NOT delete) rows on that tab if the import date (column D) is less than or equal to the current date. Basically, automate right-clicking on the row and selecting "Hide Row" based on the parameters listed above.
Completely new to Google Apps Script so any help is greatly appreciated.
=$D1<edate(today(),6)
5 Steps:
Select the cell, cell D1
Go to Format, Conditional Formatting
Under Format Rules, scroll to the bottom of the drop-down list to find "Custom Formula is"
In the text input box, copy and paste the code above
Change the text and background colours to white. To change the colour, use the Formatting options
Please note: when changing cell, change the code D1 to the current cell.
Hide old rows
function hideOldRows() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const sr = 2; //data start row
const dv = new Date().valueOf();
sh.getRange(sr,4,sh.getLastRow() - sr + 1).getValues().flat().map((d,i) => [new Date(d).valueOf(),i+sr]).filter(r => r[0] < dv).forEach(r => sh.hideRows(r[1]));
}
Sheet0 before:
Sheet0 After:
Try this in your script editor:
function hideRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Scheduled"); //get Scheduled sheet
var dateRange = sh.getRange(2, 4, sh.getLastRow()-1, 1); //get column D range
var dates = dateRange.getDisplayValues(); //get the values of column D using the range fetched above
var currentDate = new Date(); //get current date
for(var i = 0; i < dates.length; i++){
var date = new Date(dates[i][0].replace(/-/g, '\/').replace(/T.+/, '')); //convert column D values to date
if(date.valueOf() <= currentDate.valueOf()){ // check if the date is less than or equal to current date
sh.hideRows(i+2); // hide row
}
}
}
Test Data:
Output:
References:
Date.valueOf()
Sheet.hideRows()
I've set up timesheets in google sheets for my employees. Each employee has their own document, and each week at the start of the pay period, I'll make a new sheet in each document and hide the sheet from the previous week. The new sheet is named by the dates of that pay period (i.e. Feb 12 - Feb 18) I'm trying to write a script that can do this automatically for me. The script involves two functions that run on a time-based trigger. The first function is working perfectly and creates a new sheet each week at the start of the pay period with the correct dates as the name of the sheet. The second function to hide the old sheet is not working. I have the triggers staggered, so that the new sheet is created several hours before the old one should be hidden. Here is what I've tried for hiding the old sheet (the Template sheet is what is getting copied to each of the employee's documents, where B2 is the start date of the pay period and H2 is the end of the pay period).
var ss = SpreadsheetApp.openById('TEMPLATE SHEET ID').getActiveSheet()
var newName1 = Utilities.formatDate(ss.getRange("B2").getValue(),"CST","MMM dd");
var newName2 = Utilities.formatDate(ss.getRange("H2").getValue(),"CST","MMM dd");
var EMPLOYEE = SpreadsheetApp.openById('EMPLOYEE SHEET ID');
var hideEMPLOYEE = EMPLOYEE.getSheets();
for(var i =0;i<hideEMPLOYEE.length;i++){
Logger.log(i);
if(hideEMPLOYEE[i].getName()!== newName1 + "-" + newName2){
hideEMPLOYEE[i].hideSheet();
}
}
Try this:
If B2 or H2 are already dates that's okay this will just create another date from them but if they are strings that the constructor is familiar with then this will create a new date for them as well.
var ss = SpreadsheetApp.openById('TEMPLATE SHEET ID').getActiveSheet()
var newName1 = Utilities.formatDate(new Date(ss.getRange("B2").getValue()),"CST","MMM dd");
var newName2 = Utilities.formatDate(new Date(ss.getRange("H2").getValue()),"CST","MMM dd");
var EMPLOYEE = SpreadsheetApp.openById('EMPLOYEE SHEET ID');
var hideEMPLOYEE = EMPLOYEE.getSheets();
for(var i=0;i<hideEMPLOYEE.length;i++){
Logger.log(i);
if(hideEMPLOYEE[i].getName()!= newName1 + "-" + newName2){
hideEMPLOYEE[i].hideSheet();
}
}
I have a bunch of data I want to put in to multiple sheets, and to do it manually would take time and I would like to learn scripting too.
So say I have a sheet with the states in one column.
I would like to have a script make new sheets based off the values of that column, and make a hyperlink to those sheets, and sort the sheets alphabetically.
In each sheet, I need to have the A1 cell the same name as the sheet.
Here is an example of states
Any suggestions would be helpful
Edit:
This is code that can make sheets based on the values of the columns.
function makeTabs() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var last = sheet.getLastRow();//identifies the last active row on the sheet
//loop through the code until each row creates a tab.
for(var i=0; i<last; i++){
var tabName = sheet.getRange(i+2,1).getValue();//get the range in column A and get the value.
var create = ss.insertSheet(tabName);//create a new sheet with the value
}
}
(note the "sheet.getRange(i+2,1" assumes a header, so pulls from the first column, starting on the second row)
I still need to:
Add a hyper link in the index sheet to the State's sheet: example: A2 on the Index sheet
would be =HYPERLINK("#gid=738389498","Alabama")
Also I need the A1 cell of the State's page to have the same info as
the index. example: Alabama's A1 cell would be =Index!A2
You could take a look at this script:
function createSheets(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var indexSheet = ss.getSheetByName('Index');
var indexSheetRange = indexSheet.getDataRange();
var values = indexSheetRange.getValues();
var templateSheet = ss.getSheetByName('TEMPLATE_the_state');
templateSheet.activate();
var sheetIds = [],
state,
sheetId,
links = [];
for (var i = 1 ; i < values.length ; i++){
state = values[i][0];
sheetId = undefined;
try{
var sheet = ss.insertSheet(state, {template: templateSheet});
SpreadsheetApp.flush();
sheet.getRange("A1:B1").setValues([['=hyperlink("#gid=0&range=A' +(i+1)+'","go back to index")',state]]);
sheetId = sheet.getSheetId();
}
catch (e) { Logger.log('Sheet %s already exists ' , sheet)}
sheetIds.push([sheetId,state]);
}
sheetIds.forEach(function(x){
links.push(['=HYPERLINK("#gid='+x[0]+'&range=A1","'+x[1]+'")']);
});
indexSheet.getRange(2,1,links.length,links[0].length).setValues(links) // in this case it is clear to us from the outset that links[0].length is 1, so we could have written 1
}
Note that in my version, I created a template sheet from which to base all the state sheets from. This wasn't what you asked for, but I wanted to see what it would do.
The resulting sheet is here: https://docs.google.com/spreadsheets/d/1Rk00eXPzkfov5e3D3AKOVQA2UdvE5b8roG3-WeI4znE/edit?usp=sharing
Indeed, I was surprised at how long it took to create the full sheet with all the states - more than 250 secs. I looked at the execution log, which I have added to the sheet in its own tab. There it is plain to see that the code is quick, but sometimes (why only sometimes, I don't know) adding a new tab to the spreadsheet and/or flushing the formulas on the spreadsheet is very slow. I don't know how to speed it up. Any suggestions welcome. (I could try the Google Sheets API v4, probably would be much faster ... but that is much more work and tougher to do.)
I'm writing a script to loop through each sheet in one spreadsheet and copy data from specific cells into a corresponding sheet on another spreadsheet. I am getting an error on line 18 of the below code, however, stating that it can't call the getLastRow method of null. I used a couple of Logger.log lines to check my variables and see that targetSheet is coming back as null. Any advice on what I've got wrong?
//Export each sheet's daily data to another sheet *Test Version*
function exportReports() {
var sourceSS = SpreadsheetApp.getActiveSpreadsheet();
//Open Back Production Record *Test Version*
var targetSS = SpreadsheetApp.openById("1ZJKZi-UXvqyGXW9V7KVx8whxulZmx0HXt7rmgIJpUY4");
var allSourceSheets = sourceSS.getSheets();
//For-Loop to loop through hourly production sheets, running the move data for-loop on each
for(var s in allSourceSheets){
var loopSheet = allSourceSheets[s];
var loopSheetName = loopSheet.getSheetName();
var targetSheet = targetSS.getSheetByName(loopSheetName);
Logger.log(s);
Logger.log(loopSheet);
Logger.log(targetSheet);
Logger.log(loopSheetName);
var targetRow = targetSheet.getLastRow()+1;
var currentDate = Utilities.formatDate(new Date(), "GMT-5", "MM/dd/yy");
targetSheet.getRange(targetRow, 1).setValue(currentDate);
//For-Loop to move data from source to target
for(var i=6;i<=10;i++){
var sourceRange = sourceSheet.getRange(i, 2);
var targetRange = targetSheet.getRange(targetRow, i-4);
var holder = sourceRange.getValue();
targetRange.setValue(holder);
}
}
}
Per the documentation on getSheetByName, if the target sheet name does not exist, then you get null as a return value.
getSheetByName(name)
Returns a sheet with the given name.
If multiple sheets have the same name, the leftmost one is returned. Returns null if there is no sheet with the given name.
So, the desired sheet with name specified by loopSheetName does not exist in the target workbook. Perhaps someone has created a new sheet, or renamed an existing sheet in the source workbook.
You haven't asked about it, but you can improve the performance of your copy code as well, by reading the inputs as a multi-row range array, creating a row array to hold the results, and writing that once:
var sourceData = sourceSheet.getRange(6, 2, 5, 1).getValues(); // (6,2) through (10, 2)
var output = [];
// Transpose row array to column array (could use other functions, but this is easier to understand)
for(var i = 0; i < sourceData.length; ++i) { output.push(sourceData[i][0]); }
targetSheet.getRange(targetRow, 2, 1, output.length).setValues([output]); // i0 = 6 -> 6 - 4 = 2
I have a very basic script that takes a cell value, which is a date. Then renames the tab "day, dd/mm/yyyy".
The script has worked fine renaming sheets with the corresponding dates for weeks, but suddenly has an issues from what appears to be 19/03/2018 onwards. The script suddenly seems to be working with a duplicate, have lost or gained a day somewhere. I suspect I have not explained this very well, but it is driving me insane.
The script is;
function renameSheetsByTheContentsOfCellA1onEachSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var sheetNumber, sourceSheet, newSheetName;
// iterate through all sheets in the spreadsheet
for (sheetNumber = 1; sheetNumber < sourceSheets.length; sheetNumber++) {
sourceSheet = sourceSheets[sheetNumber];
// get contents of cell A2
newSheetName = sourceSheet.getRange("A2").getValue();
// rename sheet
sourceSheet.setName(Utilities.formatDate(newSheetName, "GMT", "EEEEE, dd/MM/yyyy"));
}
}
Why is the processing going wrong?
You are most likely passing an invalid date type to the .formatDate function (requires a Date, String, String) depending on how the A2 values in your sheets are formatted. To prevent this, you should convert newSheetName back to a date via new Date() in your script before passing it to the function. Of note, this shouldn't be a problem if all of your A2 values are properly formatted as dates--you might want to check that (most likely they are formatted as "Plain Text" which makes them a string).
Also, if you want your script to apply to the first sheet as well, you need to start your for loop at 0 (due to 0 index numbering). I modified your code to account for these changes below:
function renameSheetsByTheContentsOfCellA1onEachSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var sheetNumber, sourceSheet, newSheetName;
// iterate through all sheets in the spreadsheet
for (sheetNumber = 0; sheetNumber < sourceSheets.length; sheetNumber++) {
sourceSheet = sourceSheets[sheetNumber];
// get contents of cell A2
newSheetName = new Date(sourceSheet.getRange("A2").getValue());
// rename sheet
sourceSheet.setName(Utilities.formatDate(newSheetName, "GMT", "EEEEE, dd/MM/yyyy"));
}
}
Hope this helps!