How to create a new sheet in a Google Sheets with Google Apps Script?
I know it seems obvious but I just want to create a new sheet with a specific name.
Surprisingly I didn't find any clear and quick answer.
So here is my code:
function onOpen() {
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName("Name of your new sheet");
if (yourNewSheet != null) {
activeSpreadsheet.deleteSheet(yourNewSheet);
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName("Name of your new sheet");
}
Finally, note that this new sheet will be automatically the active one.
Here is a simple example:
var name = (new Date()).toLocaleDateString();
SpreadsheetApp.getActiveSpreadsheet().insertSheet(name);
I'd recommend this method.
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.insertSheet('My New Sheet');
Where 'My New Sheet' is the name you want it to be.
Here's the way I did it...
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSheet = activeSpreadsheet.insertSheet();
newSheet.setName("whatever");
I have been looking at the answers and although they answer the question of how to create a sheet and give it a name, none shows how to make that name variable by passing it as a parameter
This is an example of a function passing a variable to the createNewSheet function:
(The function that has to be executed is setNameAndCreateSheet)
function createNewSheet(sheetName){
// The sheetName parameter has to be passed to the function when it's called
let activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
let newSheet = activeSpreadsheet.insertSheet();
newSheet.setName(sheetName); // We sheet will be called as the string of the parameter
}
function setNameAndCreateSheet(){
// This will get the email of the user executing the script
const userEmail = Session.getEffectiveUser().getEmail();
// This will get the current date
const date = Utilities.formatDate(new Date(), "GMT-3", "dd/MM/yyyy");
// We are making the string that will be passed to the createNewSheet function
const sheetName = date + " - " + userEmail;
// sheetName will return something like: "04/11/2021 - youremail#gmail.com"
// This will execute the function that creates the sheet, and the name will be settled to the parameter passed (sheetName)
createNewSheet(sheetName);
}
In this example, the parameter passed to the createNewSheet function is the concatenation of these two strings (date - useremail), but it could be anything.
This function does that with more options, where you can select:
The new sheet name
Add the sheet to which spreadsheet
After (or before) which sheet (index or name)
Choose to add a counter or delete if the new name is already used before.
function addSheet(shName, ss, aftr, bfr, delMode) {
var sh = null;
// Define the optional argument ss (Spreadsheet) if missing
if (ss == null) ss = SpreadsheetApp.getActiveSpreadsheet();
// If no new name is given, use "Sheet" and deactivate delete mode
if (shName == null) {
delMode = null
shName = "Sheet"
}
if (delMode == null) {
// If delete mode is inactive, use a counter if the name already exists (like "Sheet (2)")
var n = 1;
var nShName = shName;
while (ss.getSheetByName(nShName) != null) {
n += 1;
nShName = shName + ' (' + n + ')';
}
shName = nShName;
} else {
// otherwise, delete on sight!
sh = ss.getSheetByName(shName);
if (sh != null) ss.deleteSheet(sh);
}
// Where to add the new sheet:
if (aftr == null) {
if (bfr == null) {
// If both after and before arguments are missing, add the new sheet to the end of sheets list.
n = ss.getSheets().length;
// Uncomment the next one if you want it to be added after the active sheet
// n = ss.getActiveSheet().getIndex();
} else if (typeof(bfr) == 'string') {
// If before argument was given as string (Sheet name), try to find it.
n = getSheetOrder(bfr) - 1
} else {
// otherwise use the given number
n = bfr - 1
}
} else if (typeof(aftr) == 'string') {
// If after argument was give as string (Sheet name), try to find it.
n = getSheetOrder(aftr);
} else {
// otherwise use the given number
n = aftr;
}
// Note: if both after and bfr are given, bfr is ignored.
// Fix the case where only before argument is gevin as a string,but no sheet with that name is found
if (n < 0) n = 0;
// Fix the case where the numbers given were too high
if (n > ss.getSheets().length) n = ss.getSheets().length;
// Do the action!
return ss.insertSheet(shName, n);
}
function getSheetOrder(shName, ss) {
// returns 0 if sheet is missing, or the sheet index if found.
if (ss == null) ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName(shName);
if (sh == null) return 0;
return sh.getIndex();
}
Tests:
addSheet() adds a sheet named "Sheet" (or "Sheet (2)", "Sheet (3)",...) to the end of the file (or after the active sheet if you edit the code)
addSheet("My Sheet", null, 3, null, 1) adds a sheet names "My Sheet" after the third sheet in the active spreadsheet, and deletes the old sheet with the same name if found.
Related
I want to create a script that moves data from one sheet to another when I mark it as completed in a particular column. Using some other code I found on the internet, I have this, but when I go in and change that status to completed nothing happens. The trigger page in google apps script says it's executing, but it isn't doing anything to the actual sheet. Here is the code:
function onEdit(e) {
if(SpreadsheetApp.getActiveSpreadsheet() == "Planner" && e.value == "Completed"){ //If the edit was on Planner marking the Status "Completed"
var spr = SpreadsheetApp.getActiveSpreadsheet();
var myRange = e.range.offset(0,-3,0,3).getValue() //get the information from Planner
//find the first row of Calendar where completed assignments is blank
var column = spr.getRange('O:O');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
ct++;
e.source.getSheetByName("Calendar").getRange(ct,15,1,3).setValues(myRange).getValues(); //copy the values from Planner to Calendar
e.source.getSheetByName("Planner").getRange(myRange).setValues("").getValues(); //delete values from Planner
;}
return (ct);
}
}
I assume something is wrong with it but I don't know what. I've never used apps script before so I honestly don't know what I'm doing. Here is the sheet:
Sheet
I want to move completed homework from the planner sheet to the calendar sheet when I change the status. Thanks so much for any help!!
EDIT:
I used lamblichus's code and it works great except that I still want to delete the data from the Planner Sheet after I move it. I tried this code and it didn't work:
function onEdit(e) {
const ss = e.source;
const range = e.range;
const sheet = range.getSheet();
if (sheet.getName() == "Planner" && e.value == "Completed") {
var otherData = range.offset(0,-3,1,3).getValues();
var currentClass = range.offset(0,-4).getMergedRanges()[0].getValue();
var [task,,date] = otherData[0];
var targetSheet = ss.getSheetByName("Calendar");
var targetRange = targetSheet.getRange("O1").getNextDataCell(SpreadsheetApp.Direction.DOWN).offset(1,0,1,3);
targetRange.setValues([[date,task,currentClass]]);
var initialSheet = ss.getSheetByName("Planner");
var initialRange = initialSheet.range.offset(0,-3,1,3);
initialRange.clearContent(); //delete values from Planner
}
}
Issues and solution:
There are several issues with your current code:
If you want to check the sheet name, you have to use Sheet.getName(). SpreadsheetApp.getActiveSpreadsheet() just returns the active spreadsheet, not sheet, and not its name anyway.
If you want to get values from multiple cells, you should use getValues(), not getValue().
The third parameter of offset corresponds to the number of rows of the resulting range. Therefore, it should not be 0.
The "Class" name is in a merged range, and only the top-left cell in a merged range includes the corresponding value. To get that value, you can use getMergedRanges and retrieve the first element in the resulting array. Since getValue() returns the value in the top-left cell of a range, it will return the "Class" name.
Code sample:
function onEdit(e) {
const ss = e.source;
const range = e.range;
const sheet = range.getSheet();
if (sheet.getName() == "Planner" && e.value == "Completed") {
var otherDataRange = range.offset(0,-3,1,3);
var otherData = otherDataRange.getValues();
var currentClass = range.offset(0,-4).getMergedRanges()[0].getValue();
var [task,,date] = otherData[0];
var targetSheet = ss.getSheetByName("Calendar");
var targetRange = targetSheet.getRange("O1").getNextDataCell(SpreadsheetApp.Direction.DOWN).offset(1,0,1,3);
targetRange.setValues([[date,task,currentClass]]);
otherDataRange.clearContent();
}
}
It looks like a syntax error on line 14, you put ;}, it should be }; you don't need to tell JavaScript (the coding language that AppScript is based on) when you end comments. But it likes it when you tell it when you end while loops.
Here is the updated code.
function onEdit(e) {
if(SpreadsheetApp.getActiveSpreadsheet() == "Planner" && e.value == "Completed"){ //If the edit was on Planner marking the Status "Completed"
var spr = SpreadsheetApp.getActiveSpreadsheet();
var myRange = e.range.offset(0,-3,0,3).getValue() //get the information from Planner
//find the first row of Calendar where completed assignments is blank
var column = spr.getRange('O:O');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
ct++;
e.source.getSheetByName("Calendar").getRange(ct,15,1,3).setValues(myRange).getValues(); //copy the values from Planner to Calendar
e.source.getSheetByName("Planner").getRange(myRange).setValues("").getValues(); //delete values from Planner
};
return (ct);
};
}
I would like to clear the contents of "column A" before "Update" function fills the latest data in the same column. The idea is remove any redundant data that is not required.
Usecase: Update all the tabs in one Index sheet, if people delete a sheet - that should not reflect in this Index sheet. Here is the code I have used after some research. I am new to this so need some help.
EDIT: Also how to exclude certain "Sheets" from the "Update" function so it doesn't show up in the Index column?
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Index Menu')
.addItem('Create Index', 'createIndex')
.addItem('Update Index', 'updateIndex')
.addToUi();
}
// function to create the index
function createIndex() {
// Get all the different sheet IDs
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var namesArray = sheetNamesIds(sheets);
var indexSheetNames = namesArray[0];
var indexSheetIds = namesArray[1];
// check if sheet called sheet called already exists
// if no index sheet exists, create one
if (ss.getSheetByName('index') == null) {
var indexSheet = ss.insertSheet('Index',0);
}
// if sheet called index does exist, prompt user for a different name or option to cancel
else {
var indexNewName = Browser.inputBox('The name Index is already being used, please choose a different name:', 'Please choose another name', Browser.Buttons.OK_CANCEL);
if (indexNewName != 'cancel') {
var indexSheet = ss.insertSheet(indexNewName,0);
}
else {
Browser.msgBox('No index sheet created');
}
}
// add sheet title, sheet names and hyperlink formulas
if (indexSheet) {
printIndex(indexSheet,indexSheetNames,indexSheetIds);
}
}
// function to update the index, assumes index is the first sheet in the workbook
function updateIndex() {
// Get all the different sheet IDs
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var indexSheet = sheets[0];
var namesArray = sheetNamesIds(sheets);
var indexSheetNames = namesArray[0];
var indexSheetIds = namesArray[1];
printIndex(indexSheet,indexSheetNames,indexSheetIds);
}
// function to clear index
function clearContentsOnly() {
var range = SpreadsheetApp
.getActive()
.getSheetByName("Index")
.getRange(4,2,2,2);
range.clearContent();
}
// function to print out the index
function printIndex(sheet,names,formulas) {
sheet.getRange(1,1).setValue('Task Index').setFontWeight('bold');
sheet.getRange(7,1,formulas.length,1).setFormulas(formulas);
}
// function to create array of sheet names and sheet ids
function sheetNamesIds(sheets) {
var indexSheetNames = [];
var indexSheetIds = [];
// create array of sheet names and sheet gids
sheets.forEach(function(sheet){
indexSheetNames.push([sheet.getSheetName()]);
indexSheetIds.push(['=hyperlink("https://docs.google.com/spreadsheets/d/XXXX/edit#gid='
+ sheet.getSheetId()
+ '","'
+ sheet.getSheetName()
+ '")']);
});
return [indexSheetNames, indexSheetIds];
} ```
clear contents of column A
sheet.getRange(1,1,sheet.getLastRow()).clearContent();
if you have header rows and you specify the start row:
const sr = 2;
sheet.getRange( sr, 1, sheet.getLastRow() - sr + 1).clearContent();
function updateIndex() {
// Get all the different sheet IDs
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var indexSheet = sheets[0]; //better to use the name of the index sheet - as the sheet position may get changed easily by the user
indexSheet.GetRange("A2:A").clearContent(); //add this line
var namesArray = sheetNamesIds(sheets);
var indexSheetNames = namesArray[0];
var indexSheetIds = namesArray[1];
printIndex(indexSheet,indexSheetNames,indexSheetIds);
}
Make sure you create a Sheet with the name "Index" and then the following code will update column A with all Sheets and directly link to the respective sheet. It will add new sheets with the name and url below existing entries. And if a sheet has changed its name (but not its id), then it will update the sheet name.
function updateIndex(){
const ACTIVE = SpreadsheetApp.getActive()
const spreadsheetURL = ACTIVE.getUrl()
const currentIndexSheet = ACTIVE.getSheetByName("Index")
// Empty the sheet
currentIndexSheet.getRange("A1:A").clearContent()
// We will populate this with all rows
let outputRows = []
// List Sheets which should not be listed
let skipSheets = ["Index", "Another Sheet to Skip"]
// Add Header Row
outputRows.push(['="Linked Sheet"'])
// Get all Sheet and add them to the outputRows using a Hyperlink
ACTIVE.getSheets().forEach( sheet => {
// Skip certain sheets which are defined above
if( skipSheets.indexOf(sheet.getName()) != - 1) return
outputRows.push([`=HYPERLINK("${spreadsheetURL}#gid=${sheet.getSheetId()}", "${sheet.getName()}")`])
})
// Write everything to Index
currentIndexSheet.getRange(1,1, outputRows.length).setFormulas(outputRows)
}
Hello I am currently working on a time tracking system. With the following code I track the time how long a value was in a cell. This time is recorded in another worksheet and this is done continuously by appendRow (). Now I have the problem, if several cells have one value, I only ever get the date + time in the last one. Does it work that it inserts the last value regardless of the cell?
function onEdit(e) {
addTimestamp(e);
}
function addTimestamp(e) {
var ui = SpreadsheetApp.getUi();
var ws = "Tabellenblatt2";
var ss = e.source;
var targetSheet = ss.getSheetByName("Tabellenblatt1");
var range = targetSheet.getRange(3, 2, 1000, 1);
var currentDate = new Date();
if (e.source.getActiveSheet().getName() === ws && range != "") {
var cell = ss.getActiveCell();
var val = cell.getValue();
if (val != "") {
let rowToAdd = [val, "", currentDate, ""]
ss.getSheetByName("Tabellenblatt1").appendRow(rowToAdd);
ui.alert("Test2");
} else {
var sheet = ss.getSheetByName("Tabellenblatt1");
sheet.getRange(sheet.getLastRow(), 4).setValue(currentDate);
ui.alert("Test3");
}
}
}
To explain my problem more clearly, two pictures of how the script is currently running.
If Name1 (C11) is now unsubscribed, the last date is not entered for Name1 in the first worksheet, but for Name2.
Explanation:
You can use the TextFinder class to search for the name that was removed and find the row of the specific name.
To find the old value you can use e.oldValue but that has a restriction.
Solution:
function onEdit(e) {
addTimestamp(e);
}
function addTimestamp(e) {
var ui = SpreadsheetApp.getUi();
var ws = "Tabellenblatt2";
var ss = e.source;
var targetSheet = ss.getSheetByName("Tabellenblatt1");
var range = targetSheet.getRange(3, 2, 1000, 1);
var currentDate = new Date();
if (e.source.getActiveSheet().getName() === ws && range != "") {
var val = e.range.getValue();
if (val != "") {
let rowToAdd = [val, "", currentDate, ""]
ss.getSheetByName("Tabellenblatt1").appendRow(rowToAdd);
ui.alert("Test2");
} else {
var sheet = ss.getSheetByName("Tabellenblatt1");
var dataFinder = sheet.createTextFinder(e.oldValue);
var nameRow = dataFinder.findAll()[0].getRow();
sheet.getRange(nameRow, 4).setValue(currentDate);
ui.alert("Test3");
}
}
}
Restrictions:
The e.oldValue value is undefined if you delete the content of the cell and therefore the aforementioned solution won't work.
To get the old value you need to replace it with an empty string. To do that, left click on the cell (name) you want to delete, select the text in the formula area:
and press delete to delete the text.
Last but not least, the solution assumes the names are unique.
Issue:
Since, as Mario has commented, e.oldValue is not populated when a cell content is directly removed (instead of first selecting it and then delete the content), keeping track of which value has been deleted becomes troublesome.
You need to find a way to keep track of which rows in the source sheet (Tabellenblatt2) correspond to which rows in the target sheet (Tabellenblatt1).
Solution:
You can use PropertiesService for this:
Every time a new item is written to the source sheet, use setProperty() to store a property whose key is the index of the currently edited row, and value is the index of the row appended to the target sheet.
Every time an item is removed from the source sheet, use getProperty(key) (using the current row index) to retrieve the corresponding row index in the target sheet (where the second timestamp should be written).
Write the timestamp to the row that has just been retrieved.
Code sample:
function addTimestamp(e) {
var ui = SpreadsheetApp.getUi();
var ws = "Tabellenblatt2";
var ss = e.source;
var targetSheet = ss.getSheetByName("Tabellenblatt1");
var range = targetSheet.getRange(3, 2, 1000, 1);
var currentDate = new Date();
var scriptProperties = PropertiesService.getScriptProperties();
if (e.source.getActiveSheet().getName() === ws) {
var cell = ss.getActiveCell();
var val = cell.getValue();
var sourceRowIndex = cell.getRow();
if (val != "") {
let rowToAdd = [val, "", currentDate, ""]
targetSheet.appendRow(rowToAdd);
scriptProperties.setProperty(sourceRowIndex, targetSheet.getLastRow());
} else {
var rowIndex = Number(scriptProperties.getProperty(sourceRowIndex));
if (rowIndex) targetSheet.getRange(rowIndex, 4).setValue(currentDate);
}
}
}
Note:
IMPORTANT: properties won't be stored for previously existing values. Either create those properties manually, or remove all existing values and start from scratch.
In the example above, script properties is used, but document properties and user properties could also be appropriate, depending on your current situation).
How to check if the value is exist in google spreadsheet or not using apps script
I want to check if the Sam exist in the entire spreadsheet or not using apps script. If exist I want to perform task...
function doGet(e) {
return HtmlService.createHtmlOutput("Hi there");
}
function doPost(e) {
// this is where telegram works
var data = JSON.parse(e.postData.contents);
var text = data.message.text;
var id = data.message.chat.id;
var userName = data.message.from.username;
if(/^#/.test(text)) {
var sheetName = text.slice(1).split(" ")[0];
var sheet = SpreadsheetApp.openById(ssId).getSheetByName(sheetName) ? SpreadsheetApp.openById(ssId).getSheetByName(sheetName) : SpreadsheetApp.openById(ssId).insertSheet(sheetName);
var comment = text.split(" ").slice(1).join(" ");
sheet.appendRow([userName,new Date(),id,name,comment,answer]);
}
//check if user is new in group
// this gets the range
var range = SpreadsheetApp.getActiveRange().getValues();
var searchString = "marsad01";
var isSearchStringInRange = range.some( function(row){
return row[0] === searchString
});
if(isSearchStringInRange){
// do something
sendMessage(id, answer, name);
}else{
sendGreetingMessage(id, answer, name);
}
}
is there any way how to do this
Depending on if you want to select the range or just always use the whole A:A column. In the former case, do this:
// this gets the range
var range = SpreadsheetApp.getActiveRange().getValues();
// this is what you are searching for
var searchString = "Sam";
// this is whether what you are searching for exists
var isSearchStringInRange = range.some( function(row){
return row[0] === searchString
});
// then you can proceed to do something like
if( isSearchStringInRange ){
// do something
}
Answer:
You can define a textFinder and run it over your data range.
Code:
function findSam() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var range = sheet.getDataRange();
var textFinder = range.createTextFinder('Sam');
var locations = [];
var occurrences = textFinder.findAll().map(x => x.getA1Notation());
if (occurrences == []) {
// do something if "Sam" not in sheet
}
else {
// do stuff with each range:
}
}
This code will:
Find all cells that contain "Sam" in the first sheet of the Spreadsheet
Append the Range object that contains "Sam" to an array of ranges
Map the array of ranges to an array of A1 notations which are all the cells which contain "Sam".
From here you can do what you wish with the ranges. If "Sam" is not in the sheet then occurrences will be an empty array and you can do here what you wish.
References:
Class TextFinder | Apps Script | Google Developers
I'm getting this error while running this sheet.
Cell reference out of range (line 81, file "genreportSE")
I don't know why it says it's 'out of range'.
I tried to used 'copyvalues'. I saw a script where you can't really "print" a range, but you can create another spreadsheet, copy that range, then print that sheet and delete it.
How should I accomplish this?
function genreportSE() { // This function let us read the value of a cell from a sheet and change the value of another cell in a different sheet
var ss = SpreadsheetApp.getActive(); //ss stands for spreadsheet, this is the active spreadsheet
var clientsheet = ss.getSheetByName('Clientes SE');
var gensheet = ss.getSheetByName('Generador SE');
var clienttable = clientsheet.getDataRange();
var numberofservices = clienttable.getNumRows(); //The number of services in the Clientes sheet
var error1;
var error2;
var rangetocheck1;
var rangetocheck2;
var client;
var clientname;
var i=0;
var reportswitherrors = []; //Array for faulty reports
var email ='jvaldez#galt.mx';
var subject = "Reporte de producción y consumo - " + (new Date()).toString();
var body = "TEXT" ;
for (i=0;i<=2;i++){
gensheet.getRange('B2').setValue(clientsheet.getRange(i+2,1).getValue()); //This will change the cell "B2" in "Generador SE" to the current service number for the report generation
Utilities.sleep(3000); //A timer to let the importdata function get the data from the SE server in miliseconds
client = gensheet.getRange('B4').getValue;
clientname = String(client);
rangetocheck1 = gensheet.getRange('B8:C14').getValues(); //Data range that could present calculation errors ********
rangetocheck2 = gensheet.getRange('H8:H14').getValues(); //Data range that could present calculation errors ********
if(String(rangetocheck1).indexOf('#N/A') == -1) { //This checks if there are any errors in rangetocheck1
error1 = false;
} else {
error1 = true;
};
if(String(rangetocheck2).indexOf('#N/A') == -1) { //This checks if there are any errors in rangetocheck2
error2 = false;
} else{
error2 = true;
};
if(error1||error2){
reportswitherrors.push(clientsheet.getRange(i+2,1).getValue()); //This appends the current service number to the faulty services array
} else {
// Convert individual worksheets to PDF
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export",15,60);
newSpreadsheet.getSheetByName('Sheet1').activate();
var newsheet = newSpreadsheet.getSheetByName('Sheet1');
var genRange = gensheet.getRange('A1:H50').copyValuesToRange(newsheet,0,10,0,55)
var pdf = DriveApp.getFileById(newSpreadsheet.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'Weekly Status.pdf',content:pdf, mimeType:'application/pdf'};
MailApp.sendEmail(email, subject, body, {attachments:[attach]});
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
}
};
Logger.log(reportswitherrors);
}
It appears that you've got your row & column dimensions flipped between function calls. (Because Google decided to be inconsistent with the order of them...)
This line calls create(name, rows, columns):
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export",15,60);
You've created a spreadsheet with 15 rows and 60 columns.
A bit further along, probably on line 81, copyValuesToRange(sheet, column, columnEnd, row, rowEnd) gets invoked:
var genRange = gensheet.getRange('A1:H50').copyValuesToRange(newsheet,0,10,0,55)