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!
Related
I'm using this simple script to paste data into a new spreadsheet.
In column C I have an hour in this format 05:00:00
When the script pastes the value, this hour changes! I can't understand why.
Is it about a time zone somewhere?
Thanks
j.
function copyDataToArchive() {
var sss = SpreadsheetApp.getActiveSpreadsheet();; // sss = source spreadsheet
var ss = sss.getSheetByName("1. Dispatching"); // ss = source sheet
//Get full range of data
var sRange = ss.getRange('a6:l80');
//get A1 notation identifying the range
//var A1Range = SRange.getA1Notation();
//get the data values in range
var sData = sRange.getValues();
var tss = SpreadsheetApp.openById("IDPasteSheet"); // tss = target spreadsheet
var ts = tss.getSheetByName("ARCHIVAGE"); // ts = target sheet
ts.getRange("a"+(getLastDataRowArchivage(ts)+1)+":l"+(getLastDataRowArchivage(ts)+75)).setValues(sData);
A solution was found in the comments but for future reference I'd like to add an explanation of why this happened.
When copying values, getValues() gets just the raw data within the cell without taking into account the format. Then when you use setValues(), by default the format in previously unused cells is set to "Automatic", so Sheets will just guess the format based on the values and adjust accordingly. With dates/times this can cause issues.
getDisplayValues() works because it takes into account the formatting to read exactly what's displayed and turn it into a String. Sheets is better at parsing this since it's closer to what users normally paste. I guess it does something like "prioritizing" the intended display value.
Maybe there would be some fringe scenarios where a string would not work, so I would recommend you use copyTo() instead if you plan to use the values in the range the same way and keep the formatting, since that's what the method is designed to do. In your case it probably would look like this:
function copyDataToArchive() {
var sss = SpreadsheetApp.getActiveSpreadsheet();; // sss = source spreadsheet
var ss = sss.getSheetByName("1. Dispatching"); // ss = source sheet
var sRange = ss.getRange('a6:l80');
var tss = SpreadsheetApp.openById("IDPasteSheet"); // tss = target spreadsheet
var ts = tss.getSheetByName("ARCHIVAGE"); // ts = target sheet
sRange.CopyTo(ts.getRange("a"+(getLastDataRowArchivage(ts)+1)+":l"+(getLastDataRowArchivage(ts)+75)));
I am trying to copy data from 1 spreadsheet to another, I have successfully implemented something i found online that works with a specific range
function cloneGoogleSheet() {
// source doc
var sss = SpreadsheetApp.openById("spreadsheetkey1");
// source sheet
var ss = sss.getSheetByName('_tab_name_source');
// Get full range of data
var SRange = ss.getRange(7,3,5,1);
// get A1 notation identifying the range
var A1Range = SRange.getA1Notation();
// get the data values in range
var SData = SRange.getValues();
// target spreadsheet
var tss = SpreadsheetApp.openById("spreadsheetkey2");
// target sheet
var ts = tss.getSheetByName('tab_name_destination');
// Clear the Google Sheet before copy
//ts.clear({contentsOnly: true});
// set the target range to the values of the source data
ts.getRange(A1Range).setValues(SData);
};
The above piece coding work perfectly however I need to copy 18 different ranges that i cant just merge into 1 range. I considered the option of using the above however "multiplying" it 18 times for each range that however seems like a very inelegant solution.
I found a working solution that works if it stays within the same spreadsheet since it uses copyto instead of get/set values. The values part works perfectly since it doesnt mess with merge cells formatting. I have been struggling past 2-3 hours in merging the below-working code with elements from the first code to make a working script.
function test () {
try {
var spread = SpreadsheetApp.openById("spreadsheetkey");
var sheet = spread.getSheetByName("tab_name_source");
var rlist = sheet.getRangeList(["c7:c11", "g7:g11", "k7:k11"]);
sheet = spread.getSheetByName("tab_name_destination");
for( var i=0; i<rlist.getRanges().length; i++ ) {
var r1 = rlist.getRanges()[i];
var r2 = sheet.getRange(r1.getA1Notation());
r1.copyto(r2);
}
}
catch(err) {
Logger.log(err);
}
}
I tried initially to adapt the 2nd piece of coding to using setvalues however i had not been able to succesfully implement the part of getvalues within the scope of this code. I figured once I got this piece of code working with get and set values instead of Copyto i would only need to add the spreadsheetid of the other spreadsheet to get the final result
Try this:
function myFunction() {
var sourceSS = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = sourceSS.getSheetByName("sheetname");
var targetSS = SpreadsheetApp.openById("spreadsheet id here");
var targetSheet = targetSS.getSheetByName("Sheet1");
var ranges = ["C7:C11", "G7:G11", "K7:K11"];
ranges.forEach(range => {
var data = sourceSheet.getRange(range).getValues();
targetSheet.getRange(range).setValues(data);
})
}
Source sheet:
Destination sheet:
References:
setValues()
getValues()
I have a range of cells in Google Sheets, some of them have notes attached.
If there's a note attached to a cell, I need to put the note in a separate cell, and put the location of that note in another cell.
I found this script elsewhere:
function getNote(cell)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange(cell)
return range.getNote();
}
but when I try to use it I get an error "Exception: Range not found (line 12)."
But this script only gets me halfway there as it only gets the note and puts it in a cell. I also need to know what cell the note came from.
Any help is greatly appreciated.
In the script above the function getNote() expects the paramter cell
If you just run the script without calling getNote() from another function / an environment where it gets a values for cell assigned, the script will fail wiht the error you obtained.
Indeed, this script doe snot not meet your needs. What you probably want is to screen all your cells for the one that have notes.
What you need to decide is into which cells you want to put the note and the cell notation.
Below is a sample that you need to adapt for your needs:
function getNotes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//if you have only one sheet in the spreadsheet, otherwise use ss.getSheetByName(name);
var sheet = ss.getActiveSheet();
var range = sheet.getDataRange();
var results = range.getNotes();
for (var i = 0; i < results.length; i++) {
for (var j = 0; j < results[0].length; j++) {
//if a not empty note was found:
if(results[i][j]){
var note = results[i][j];
var cell = range.getCell(i+1, j+1);
var notation = cell.getA1Notation();
//adjust the offset as function of the column / row where you want to output the results
cell.offset(0, 1).setValue(note);
cell.offset(0, 2).setValue(notation);
}
}
}
}
Important references:
getNotes()
getA1Notation()
getDataRange()
getCell(row, column)
offset(rowOffset, columnOffset)
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.)
Situation:
I have a spreadsheet with 20 sheet.
I have other script that copy sheets from other spreadsheet every days to this spreadsheet-
I need to delete every days some specific sheet from a particular spreadsheet.
Problem:
When the script ends to clear the sheets, the spreadsheet hangs and I have to exit and re-enter to the spreadsheet.
I'll appreciate if anyone can help to tunning this script to work without hangging the spreadsheet.
Script:
function shellDeleteSheets(){
var sheets = ['Sheet1','Sheet2','Sheet3','Sheet4','Sheet5'];
for (var s in sheets){
deleteSheetName(sheets[s]);
}
}
function deleteSheetName(stname) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName(stname);
if(!sh) {
return;
}
ss.setActiveSheet(sh);
ss.deleteActiveSheet();
Utilities.sleep(400);
SpreadsheetApp.flush();
}
Try this version I use without issue
function DeleteSheets(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ['sheet1','sheet2','sheet3'];
var numberOfSheets = ss.getSheets().length;
for(var s = numberOfSheets-1; s>0 ; s--){ // in my case I never delete the first sheet
SpreadsheetApp.setActiveSheet(ss.getSheets()[s]);
var shName = SpreadsheetApp.getActiveSheet().getName();
if(sheets.indexOf(shName)>-1){
var delSheet = ss.deleteActiveSheet();
Utilities.sleep(500);
}
}
SpreadsheetApp.setActiveSheet(ss.getSheets()[0]);// send me back to first sheet
}
You could of course use the array of names as an argument for the function or - that's what I do in some cases - give the names of the sheet I need to keep, in this case the if condition is just different .
the sleep can be 400 mS, not sure it makes any difference, I use 500 because at some time I found it more reliable... and I (try to) never change a working solution ;-)
EDIT following your comment :
to make active the sheet called 'Updated' just change the last line of the code like this :
SpreadsheetApp.setActiveSheet(ss.getSheetByName('Updated'));
please note also that I moved this line in the original code, outside of the loop, sorry for this small error ^^