Google Sheets Script: Insert New Row In Named Range - google-apps-script

I use Named Ranges a lot, for sheets Data Validation etc. I'm adding 'Saved Comparisons' (cell H3) to a stock comparison sheet:
Although untested yet, I've almost created a function to Save or Update the current comparison to a Named Range Compare_Saved:
However, I'm stuck at how to add a New Row to the Named Range, and/or then select the required Row in the Range to update it. The problem is described in the indented section of the code below, commented with // ***** (may need to scroll down to see it):
function compareSaveNew() { compareSave('new'); } // button call
function compareSaveUpdate() { compareSave('update'); } // button call
function compareSave(mode){
const srcSheet = 'Compare';
const dataSheet = 'Data';
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sSrc = ss.getSheetByName(srcSheet);
const sData = ss.getSheetByName(dataSheet);
const ui = SpreadsheetApp.getUi();
var promptMsg='', data, rv, rUpdate;
// Check/confirm if adding new, or updating
if ( mode == 'update' && src.getRange('Compare_SavedUsed') == 'Select Comparison' ) {
promptMsg = 'A Saved Comparison has not been selected. Cancel, or enter a New Name: ';
} else if ( mode == 'new' && src.getRange('Compare_SavedUsed') != 'Select Comparison' ) {
promptMsg = 'A Saved Comparison has been selected. Cancel and use [UPDATE],\nOR\n' +
'enter a New Name if you want to Save a new Comparison based on this one: ';
} else {
promptMsg = 'New Name for Saved Comparison: ';
}
var newName = ui.prompt('New Name for Saved Comparison: ');
if (!newName) {return;} // Exit, Save/Update cancelled
// Check newName is unique
data = sData.getRange('Compare_Saved');
for(var i = 0; i<data.length;i++){
if( data[i][0] == newName ) { rv = true; break; }
}
if (rv) {ui.alert('"' + newName + '" aleady exists.'); return;} // Exit, new name exists
// *****
// Create New row in Named Range
if (mode=='new') {
// add row to range - how ???
}
// Get new/existing row as range from Named Range to update
// var rUpdate = sData.getRange( ??? );
// *****
var newSave=[];
newSave.push([
newName,
'', // left blank, so name can overflow to this col in Data sheet
sSrc.getRange('Compare_Period'),
sSrc.getRange('Compare_Frequency'),
sSrc.getRange('Compare_From'),
sSrc.getRange('Compare_To'),
sSrc.getRange('Compare_Index'),
sSrc.getRange('Compare_Stock1'),
sSrc.getRange('Compare_Stock2'),
sSrc.getRange('Compare_Stock3'),
sSrc.getRange('Compare_Stock4')
]);
rUpdate.setValues(newSave);
return true;
}
Apologies if this post is a bit verbose, but any pointers would be appreciated.

How about remove() and addNew() namedRanges

Adapted code I found by #Cooper (thanks!) for my specific needs, which returns new row num, or row num to be updated, depending on mode:
function compareAddOrFindRowInNR(namedRange,newName,mode){
// Purpose: Add a new row in Named Range and return row num, OR return existing row num
// Credit: #Cooper at https://stackoverflow.com/a/60011251/190925
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the NR
var nrAll = ss.getNamedRanges()
for (var i=0; i<nrAll.length; i++) {
if( nrAll[i].getName() == namedRange ) {
var nr = nrAll[i];
var h = nr.getRange().getHeight(); Logger.log('h: '+h)
var row= nr.getRange().getRow(); Logger.log('row: '+row)
var w = nr.getRange().getWidth(); Logger.log('w: '+w)
var col= nr.getRange().getColumn(); Logger.log('col: '+col)
var sh = nr.getRange().getSheet(); Logger.log('sh: '+sh)
// Create new row in NR, or find existing row
if (mode=='new') {
var updateNR = sh.getRange(row,col,h+1,w);
ss.setNamedRange(namedRange,updateNR); // doesn't set Sheet?
var rowUpdate = row+h;
} else {
data = nr.getRange().getValues();
for (var i=0; i<data.length; i++) {
Logger.log(data[i][0]);
if( data[i][0] == newName ) {
var rowUpdate = row+i;
break;
}
}
}
break;
}
}
Logger.log('rowUpdate: '+rowUpdate);
return rowUpdate;
}

Related

split the sheet into different workbooks by column values with dynamic range in apps script

I want to split the google sheet into different workbooks, not tabs in the same workbook based on values in column A. Although I have got a script that splits the data into different workbooks but the data range in it is not dynamic like the number of columns to be added into each workbook are fixed. I want them to be dynamic like till the last column of the data range. I have tried a lot to make it dynamic by adding loops but it shows The number of columns in the data does not match the number of columns in the range. The data has 1 but the range has 12. this error. The data in the log has almost no difference for the fixed range (which is working fine) and for the dynamic range that I have tried to it But don't know why it is showing error. Have got stuck into it. any help will be highly appreciated.
This the function that I am trying.
function splitSheets() {
var theWorkbook = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = theWorkbook.getSheetByName("Master");
var slc = theSheet.getDataRange().getLastColumn()
var slcv = theSheet.getRange("B1:B" + slc).getValues()
var sheets = theWorkbook.getSheets();
for (i = 0; i < sheets.length; i++) {
switch(sheets[i].getSheetName()) {
case "Master":
break;
default:
theWorkbook.deleteSheet(sheets[i]);
}
}
var key = theSheet.getRange("A:A").getValues();
var rows = theSheet.getDataRange().getValues();
var headerFormat = theSheet.getRange("2:2").getValues();
var folderId = '16XVypjB5_PWe2PaBIREpDGCNQlZuWL4k'
var completedSheets = [];
for (var i = 2; i < key.length; i++) {
// if(completedSheets.includes('Blank') && key[i][0] === ""){
// }else{
if(!completedSheets.includes(key[i][0]) ) {
if (key[i][0] === "") {
var name = 'Blank'
var resource = {
title: name,
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var insertedFile = Drive.Files.insert(resource)
var csid = insertedFile.id
var currentSheet = SpreadsheetApp.openById(csid).getSheetByName("Sheet1")
// var currentSheet = theWorkbook.insertSheet("Blank");
} else {
var name = key[i][0]
var resource = {
title: name,
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var insertedFile = Drive.Files.insert(resource)
var csid = insertedFile.id
var currentSheet = SpreadsheetApp.openById(csid).getSheetByName("Sheet1")
// var currentSheet = theWorkbook.insertSheet(key[i][0]);
}
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++) {
var rown = []
for(var c = 0; c < slcv.length; c++){
// some other trials
// if((rows[j][0] == key[i][0]) || (rows[j][0] === '' && currentSheet.getName() == "Blank")){
// theNewRows[b]=[];
// theNewRows[b].push (
// rows[j][c].toString()
// This although adds the data and range dynamically but also shows the mentioned error.
rown.push(rows[j][c])
// );
// b++;
// }
}
if((rows[j][0] == key[i][0]) || (rows[j][0] === '' && currentSheet.getName() == "Blank")){
theNewRows[b]=[];
theNewRows[b].push (
rown.toLocaleString()
// These are the fixed column for data rnage
// rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7],rows[j][8],rows[j][9],rows[j][10],rows[j][11]
);
b++;
}
Logger.log(rown)
}
Logger.log(theNewRows)
// Logger.log(theNewRows)
currentSheet.getRange("1:1").setValues(headerFormat)
var outrng = currentSheet.getRange(2,1,theNewRows.length, slc);//Make the output range the same size as the output array
outrng.setValues(theNewRows);
currentSheet.autoResizeColumns(1, slc);
if(currentSheet.getSheetName() == 'Blank') {
completedSheets.push('Blank');
last = "Blank";
}else{
completedSheets.push(key[i][0])
last = key[i][0]
// }
}
}
}
SpreadsheetApp.setActiveSheet(theWorkbook.getSheetByName('Master'));
}
I overhauled and improved your script to be more readable and use a lot less Spreadsheet calls by using array methods instead.
Script:
function splitSheets() {
var folderId = '*** FOLDER ID ***';
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheets = spreadsheet.getSheets();
var sheet = spreadsheet.getSheetByName('Master');
// delete sheets that are not named 'Master'
sheets.forEach(sheetIter => {
if(sheetIter.getSheetName() != 'Master')
spreadsheet.deleteSheet(sheetIter);
});
var data = sheet.getDataRange().getValues();
// remove 1st row (blank row)
data.shift();
// remove 2nd row from data and assign as headers
var headers = data.shift();
// get unique list of sheet names from column A
var sheetNames = data.map(row => row[0]).filter(onlyUnique);
// loop those unique sheetNames
sheetNames.map(sheetName => {
// filter data by getting only rows with same column A and sheetName
var outputData = data.filter(row => row[0] == sheetName);
// add header from data filtered
outputData.unshift(headers);
var resource = {
title: sheetName || 'Blank',
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var file = Drive.Files.insert(resource);
var currentSheet = SpreadsheetApp.openById(file.id).getSheetByName('Sheet1');
// write data filtered with the header
currentSheet.getRange(1, 1, outputData.length, outputData[0].length).setValues(outputData);
// resize the columns
currentSheet.autoResizeColumns(1, outputData[0].length);
});
}
// function to get unique values from array using filter
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
Sample Output:

Find value in every cell of SpreadSheet

Cannot make the code works, i want to check for every cell in spreadsheet for a value and paste the entire row that contains the value below.
var rowValue = spreadSheetData.getValues()
var findText = spreadSheetData.createTextFinder(usedEmail)
var bool
var mailFinder = findText.findAll().map(x => x.getA1Notation())
Logger.log(mailFinder)
if (choice == "Sí") {
var i = mailFinder.indexOf(usedEmail)
if (i != "") {
rowValue[i][0] = new Date()
var rowValues = rowValue[i]
bool = true
}else{
bool = false
}
}
If i do like the code above, it works and gives me true or false depending of the result but the "i" var throws me "undefined" because i'm not using the rowValue variable. In the other hand if i add a "for" iteration like:
for(i = 0; i < rowValue.length; i++){
rowValue[i][0] = new Date()
var rowValues = rowValue[i]
}
It copies the last row and that's not the approach i want because i want to copy the row where the value is.
Example sheets:
| header1 | header2 |
| -------- | ------------------------- |
| 231232132| valueiwanttofind#gmail.com|
| asdasdas | row |
| header1 | header2 |
| ------------------------- | ------------------------- |
| 231232132 | row |
| valueiwanttofind#gmail.com| another row |
the valueiwanttofind#gmail.com can be in any cell. I tried another approach but without luck...
for(var i = 1; i < rowValue.length; i++) {
for(var n = 1; n < rowValue[i].length; n++) {
var findText = rowValue[i]
Logger.log(findText)
if (choice == "Sí") {
if (findText.indexOf(usedEmail)) {
rowValue[i][0] = new Date()
var rowValues = rowValue[i]
bool = true
}else{
bool = false
}
}
}
}
UPDATE:
Thanks for your help, i managed to implement what i needed, i will explain briefly so if anyone comes with the same problem
function getItems(email,usedEmail, choice, name, gender){ //This functions brings vars as parameters from main function who map every row
var spreadSheet = SpreadsheetApp.openById(form.getDestinationId())
var spreadSheetData = spreadSheet.getDataRange()
var rowValue = spreadSheetData.getValues()
var bool
rowResult = rowValue.find(r => r.includes(usedEmail)) //I didn't know how to use find, instead i used findAll with textfinder
if (choice == "Sí") {
if (rowResult) {
var rowValues = rowResult //pass rowValues with the result above
bool = true
}else{
bool = false
}
}
for(i = 0; i < rowValue.length; i++){
rowValue[i][0] = new Date() //I used just in case to put a the current date when the form is sent to not copy that value(overwrite).
}
Logger.log(usedEmail)
Logger.log(bool)
if (bool == true){
setItems(spreadSheet, rowValues)
//RespuestaAutomatica(name, email, gender) is another function i have
Logger.log("Inscripción válida")
}else{
Logger.log("Inscripción fallida, email no registrado")
}
}
function setItems(spreadSheet, rowValues){ //the function created to append the rows at the next one.
spreadSheet.appendRow(rowValues)
}
Thanks for your help!
SUGGESTION:
Although it is still unclear what's the specific purpose this line of code below, I'm hunching that your main goal is to get the row numbers where matches are found on your sheet. But feel free to let us know if we misunderstood your post.
Line of code that is unclear:
rowValue[i][0] = new Date()
var rowValues = rowValue[i]
Why are you assigning a date value on an array data in rowValue[i][0]? What's the specific purpose of this?
Perhaps you can try this tweaked script:
//Initialized these lines below for the code to work
var spreadSheetData = SpreadsheetApp.getActiveSpreadsheet();
var usedEmail = "valueiwanttofind#gmail.com";
var choice = "Sí";
function testFunction() {
var rowValue = spreadSheetData.getDataRange().getValues();
var findText = spreadSheetData.createTextFinder(usedEmail);
var bool;
var mailFinder = findText.findAll().map(x => x.getA1Notation().toString())
if (choice == "Sí" & mailFinder.length != 0) {//Make sure mailfinder is not null
var rowValues = mailFinder;
rowValues = rowValues.map(function(x){ return x.replace(/[^abc]/,'') }); //Get the row numbers of the macthed rows that are orginally in A1Notation format
Logger.log("Found \""+""+usedEmail+"\" on Row #:\n" + rowValues);
bool = true;
}else{// If mailFinder has no data found
bool = false
}
Logger.log(bool);
}
Sample Result:
Test Sheet that I have used:
Something like this?
function myFunction() {
var email = 'valueiwanttofind#gmail.com'; // the wanted email
var sheet = SpreadsheetApp.getActiveSheet(); // the current sheet
var data = sheet.getDataRange().getValues(); // all the data from the sheet
var row = data.find(r => r.includes(email)); // the row with the email or undefined
if (row) sheet.appendRow(row); // append the row at the end of the sheet
}
It copies (appends) the first row that contains the email at the end of the sheet.
(If you want the index of the row, you can use findIndex() instead of find())
If you want to append all the rows that contain the email you can use filter() instead of find():
function myFunction() {
var email = 'valueiwanttofind#gmail.com'; // the wanted email
var sheet = SpreadsheetApp.getActiveSheet(); // the current sheet
var data = sheet.getDataRange().getValues(); // all the data from the sheet
var rows = data.filter(r => r.includes(email)); // the rows with the email
// append the array of rows at the end of the sheet
if (rows) rows.forEach(row => {
sheet.appendRow(row); // <--- it's ok if there are a few rows only
SpreadsheetApp.flush();
});
}
(The script should be improved if you have many rows with the email).

Change cell value on button click to column value based on a criteria

I would like to change the value in cell G2 by clicking on a button (D3). The (A-column) values that are being looped through should meet the criteria "yes" in the column B.
The OP's goal is to loop through the list of names stopping on each name with a value of "yes".
Obviously this requires skipping over the names with a value of "no". And there is a special challenge when dealing with the last name in the list AND that name having a "select" value of "no". In that case, the loop should "restart" at the top of the list.
The following code address each of these contingencies.
Using the sample data shown below; the code should stop on Paul, Dave, Brett and Ted.
If the value in Cell G2 is "Ted", then the next selected name should be Paul.
Sample data screenshot
function so5794149403() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('57941494');
var rg=sh.getRange(2,1,sh.getLastRow()-1,2);
var vA=rg.getValues();
var vAnum = vA.length;
var names = sh.getRange(2,1,sh.getLastRow()-1,1).getValues();
//flatten the array
var users = names.map(function (row) { return row[0]; });
// set some variables
var i=0;
var failedNTLN = 0; // fail but Not The Last Name
var modifiedTLN = 0;// fail but The Last Name
//Loop through names
for (i;i<names.length;i++){
//Logger.log("DEBUG:0:i = "+i+" and failedNTLN = "+failedNTLN+" and modifiedTLN = "+modifiedTLN);
if (failedNTLN ==99){
// if last name was a failure AND it was not the Last name, then do nothing
}
else {
// get the name in Cell G2
var g2=sh.getRange("G2").getValue();
// Logger.log("DEBUG:1 currently selected name = "+g2);
// get the position of G2 in the list of names
var Pos = users.indexOf(g2);
// Logger.log("DEBUG:1 position = "+Pos);
if (modifiedTLN == 99){
// if last name was a failure AND it was The Last Name
i=0;
Logger.log("DEBUG:1: modifiedTLN is "+modifiedTLN+", and i = "+i);
}
else{
// test if this is the last name in the list
if (Pos+1 == names.length){
// this is the last name
i=0;
failedNTLN = 0;
modifiedTLN = 0;
Logger.log("DEBUG:1: Pos is last position. i = "+i)
}
else
{
// this is not the last name
i = Pos+1;
// Logger.log("DEBUG:1: Pos is NOT the last position. set i = "+i)
}
}
}
// Logger.log("DEBUG:1: settings are: i="+i+", next status = "+vA[i][1].toString().toLowerCase()+", next name = "+vA[i][0]+", g2 = "+g2);
// logic statement if status = yes, and name isn't = G2
if(vA[i][1].toString().toLowerCase()=='yes' && vA[i][0]!=g2) {
// Logger.log("DEBUG:2: IF - outcome success: i="+i+", status = "+vA[i][1].toString().toLowerCase()+", name = "+vA[i][0]+" does not equal "+g2);
sh.getRange("G2").setValue(vA[i][0]);
// Logger.log("DEBUG:2: setting G2 to "+vA[i][0]+", i = "+i+" and break")
failedNTLN=0;modifiedTLN=0;
break;
}
else if (users.indexOf(vA[i][0]) == (names.length-1)){
// the next name is the last name
// Logger.log("DEBUG:3: the next name is the last name");
modifiedTLN = 99;
failedNTLN=0;
i=0;
// Logger.log("DEBUG:3: position = "+users.indexOf(vA[i][0]))
// Logger.log("DEBUG:3: the next name: "+users.indexOf(vA[i][0])+" is the last name; i = "+i+"; set modifiedTLN to "+modifiedTLN+"; set failedNTLN to "+failedNTLN);
}
else{
// the next name is NOT the last name
failedNTLN = 99;
modifiedTLN = 0;
//Logger.log("DEBUG:4: the next name: "+users.indexOf(vA[i][0])+" is NOT the last name; i = "+i+"; set failedNTLN to "+failedNTLN+"; set modifiedTLN to "+modifiedTLN);
}
}
}
Try this:
function makingChange() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var rg=sh.getRange(2,1,sh.getLastRow()-1,2);
var vA=rg.getValues();
var rg2=sh.getRange('G2');
var vg2=rg2.getValue();
var rgi=sh.getRange('H2');
var vi=rgi.getValue();
var sObj={nA:[]};
var startIdx=!vi?0:vi;
if(startIdx>=(vA.length-1))startIdx=0;
for(var i=startIdx;i<vA.length;i++) {
if(!vg2 && vA[i][1].toString().toLowerCase()=='yes'){
rg2.setValue(vA[i][0]);
sh.getRange(i+2,1).activate();
sh.getRange('H2').setValue(i);
break
}
if(vg2 && vA[i][1].toString().toLowerCase()=='yes' && vA[i][0]!=vg2) {
rg2.setValue(vA[i][0]);
sh.getRange(i+2,1).activate();
rgi.setValue(i);
break;
}
if(vg2 && vA[i][1].toString().toLowerCase()=='no' && i==vA.length-1) {
rg2.setValue('');
rgi.setValue('');
makingChange();//Thats right this runs the script over again
}
}
}
Run this first
It will give you a dialog with a button on it for running the above script. Which will then cycle you through the lines with Yes in column 2.
function makeDialog() {
var html='<input type="button" value="Run" onClick="google.script.run.makingChange();" />';
var userInterface=HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showSidebar(userInterface);
};
The Spreadsheet:

Google Script to Match cell value and copy Paste related data in different tab

Click here for Sample Sheet
I need a solution that matches a cell value (Sheet1! Q5) to a range in another tab/sheet (NegotiationData! A1:O1) and paste the relevant data fetched from the first sheet under the designated columns of the second sheet under the matched value.
For example, if Sheet1!Q5 matches with the name in NegotiationData! A1 then do the following
Fetch Sheet1! R6 and paste in NegotiationData!A3:A
Fetch Sheet1! Q6 and paste in NegotiationData!B3:B
Fetch Sheet1! Q7 and paste in NegotiationData!C3:C
Also, each time the script runs it should not overwrite data but find the next empty row and paste the values.
I have an incomplete script that I'm trying to achieve the above from my research from various posts but since I'm just a week old to coding I'm not able to go any further than where I have got with the below script.
I'm not finding how to match the value and fetch the relevant data and paste them below the matched value.
Please help!
The Incomplete / Incorrect Script (File Name: NegotiationSubmit)
function submitNegotiation() {
var sh, id, v, estNum, negotiation, negoNotes, i;
sh = SpreadsheetApp.getActive();
id = sh.getSheetByName('Sheet1').getRange('Q5').getValue();
v = sh.getRange('R6').getValue();
estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);
negoNotes = sh.getRange('Q7').getValue();
negotiation =sh.getSheetByName('NegotiationData').getRange('A1:O');
if(v && estNum) {
negotiation.setValues(negotiation.getValues()
.map(function (r, i) {
if (r[0] == id) {
r[1] = v;
r[2] = estNum;
r[3] = negoNotes;
}
return r;
})
)
}
}
How about this modification?
Modification points :
Retrieve values of "Q5:R7" at once, and the values are converted to the import values.
Use the destructuring assignment for retrieving each value.
Import the converted values using the number of column retrieved by ids[0][i] == id.
Modified script :
function submitNegotiation() {
var id, estNum, v, negoNotes;
var sh = SpreadsheetApp.getActive();
var values = sh.getSheetByName('Sheet1').getRange("Q5:R7").getValues();
[id] = values[0];
[estNum, v] = values[1];
[negoNotes] = values[2];
// estNum = Number(estNum.split(" ")[1]); // If you want to use "estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);", please use this line.
var sh2 = sh.getSheetByName('NegotiationData');
var ids = sh2.getRange("A1:O1").getValues();
for (var i=0; i<ids[0].length; i++) {
if (ids[0][i] == id) {
sh2.getRange(sh2.getLastRow() + 1, i + 1, 1, 3).setValues([[v, estNum, negoNotes]]);
}
}
}
Note :
I was confused to the following points.
In your script, estNum is Number(sh.getRange('Q6').getValue().split(" ")[1]);. But in your sample spreadsheet, Estimate 1 of cell "Q6" is used.
I commented this in modified script.
In your sample spreadsheet, "Story ID" is 1. But in your script, it's US-001 of cell "R6".
In this modified script, US-001 of cell "R6" was used.
If I misunderstand your question, I'm sorry.
Edit :
function submitNegotiation() {
var id, estNum, v, negoNotes;
var sh = SpreadsheetApp.getActive();
var values = sh.getSheetByName('Sheet1').getRange("Q5:R7").getValues();
[id] = values[0];
[estNum, v] = values[1];
[negoNotes] = values[2];
estNum = Number(estNum.split(" ")[1]); // If you want to use "estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);", please use this line.
var sh2 = sh.getSheetByName('NegotiationData');
var ids = sh2.getRange("A1:O1").getValues();
for (var i=0; i<ids[0].length; i++) {
if (ids[0][i] == id) {
var temp = sh2.getRange(1, i + 1, sh2.getLastRow(), 3).getValues();
for (var j=temp.length-1; j>=0; j--) {
if (temp[j].join("") != "") {
sh2.getRange(j + 2, i + 1, 1, 3).setValues([[v, estNum, negoNotes]]);
break;
}
}
}
}
}
I realize that this does not resemble your code. Sorry about that. I'm learning too. But I'm putting it up anyway to provide an alternative method that includes finding the last row of the appropriate column...
function submitNegotiation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Sheet1');
var negotiationData = ss.getSheetByName('NegotiationData');
// Sheet1 variables
var user = sheet1.getRange('Q5').getValue();
var storyId = Number(sheet1.getRange('R6').getValue().split("-")[1]);
var estimateNum = sheet1.getRange('Q6').getValue();
var note = sheet1.getRange('Q7').getValue();
var pointers = [storyId, estimateNum, note];
// NegotiationData variables
var range = negotiationData.getDataRange().getValues();
var columns = negotiationData.getLastColumn();
var users = negotiationData.getRange(1, 1, 1, columns).getValues();
for(var i = 0; i < columns; i++) {
// match user with users to get column number
if(users[0][i] == user) {
var col = negotiationData.getRange(1, i + 1).getColumn();
// count rows in col
var rowCount = 1;
for(var i = 1; i < range.length; i++) {
if (range[i][col - 1] != "") {
rowCount++;
}
}
// assign pointers
var newRow = rowCount + 1;
for(var j = 0; j < pointers.length; j++) {
negotiationData.getRange(newRow, col, 1, 1).setValue(pointers[j]);
col++;
}
}
}
}

Create a new sheet in a Google Sheets with Google Apps Script

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.