Google spreadsheet copy values from one row to another sheet - javascript

I have a Google Spreadsheet with 3 different sheets that you can see here.
What I would like to do is per each person in the 'person' sheet, create a row of the person in the 'stacs' sheets that will be repeated as many times based of the number of 'meals' that we have in the 'meals' sheet. So, per each person, I want to create personA + meal1 in row #1, personA + meal2 in row #2, personA + meal3 in row #3, etc.
I figured it out how to do this repeating the same code over and over again. The code works but it's very inefficient. I guess that there has to be a way of doing the same repetition that I'm doing in a much cleaner and simpler way. I'm just learning how to you use some scripting to improve my spreadsheet so any help with this will be very welcome!
Some Caveats:
If you take a look at the spreadsheet, you will see that I'm using formulas in some cell to calculate the last row with value or the sizes of the ranges.
In the 'meals' tab I have defined a name range with the meals. I'm not very proud of this, because the list of the meal will grow so I wonder if there is a more dynamic way of dealing with this instead of adjusting the range every time I add a new meal.
Here is the code that you also find in the spreadsheet:
function copyValuesA() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("names");
var name = sheet.getRange("A2");
var sheetMeals = ss.getSheetByName("meals");
var rangeMeals = sheet.getRange("mealsNames");
var sheetstacs = ss.getSheetByName("stacs");
var lastRow = sheetstacs.getRange("B1").getValue();
var mealCount = sheetstacs.getRange("D1").getValue();
var lastRowMeals = sheetstacs.getRange("C1").getValue();
var rangeOrigname01 = sheetstacs.getRange("A" + lastRow + ":A" + mealCount);
var rangeOrigname02 = sheetstacs.getRange("A1");
name.copyTo(rangeOrigname01);
var rangeDestMeals01 = sheetstacs.getRange("B" + lastRowMeals);
rangeMeals.copyTo(rangeDestMeals01);
}
function copyValuesB() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("names");
var name = sheet.getRange("A3");
var sheetMeals = ss.getSheetByName("meals");
var rangeMeals = sheet.getRange("mealsNames");
var sheetstacs = ss.getSheetByName("stacs");
var lastRow = sheetstacs.getRange("B1").getValue();
var mealCount = sheetstacs.getRange("D1").getValue();
var lastRowMeals = sheetstacs.getRange("C1").getValue();
var rangeOrigname01 = sheetstacs.getRange("A" + lastRow + ":A" + mealCount);
var rangeOrigname02 = sheetstacs.getRange("A1");
name.copyTo(rangeOrigname01);
var rangeDestMeals01 = sheetstacs.getRange("B" + lastRowMeals);
rangeMeals.copyTo(rangeDestMeals01);
}
function copyValuesC() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("names");
var name = sheet.getRange("A4");
var sheetMeals = ss.getSheetByName("meals");
var rangeMeals = sheet.getRange("mealsNames");
var sheetstacs = ss.getSheetByName("stacs");
var lastRow = sheetstacs.getRange("B1").getValue();
var mealCount = sheetstacs.getRange("D1").getValue();
var lastRowMeals = sheetstacs.getRange("C1").getValue();
var rangeOrigname01 = sheetstacs.getRange("A" + lastRow + ":A" + mealCount);
var rangeOrigname02 = sheetstacs.getRange("A1");
name.copyTo(rangeOrigname01);
var rangeDestMeals01 = sheetstacs.getRange("B" + lastRowMeals);
rangeMeals.copyTo(rangeDestMeals01);
}
function copyValuesAll() {
copyValuesA();
copyValuesB();
copyValuesC();
}

If your interested in a dynamic formula to accomplish this:
in your stacs sheet in column A cell 2 put:
=TRANSPOSE(SPLIT(JOIN(";",ARRAYFORMULA(REPT(A2:A&";",COUNTA(meals!$A$2:$A)))),";"))
and in cell B2:
=TRANSPOSE(SPLIT(ARRAYFORMULA(REPT(JOIN(";",meals!$A$2:$A$8),COUNTA(names!A2:A))),";"))
doing this will automatically account for new values

Related

Hello. I am a begineer to javascript and google app script.I have done my coding to send emails by google sheets but the emails are not sending

Here is my coding. Help me out.
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").activate();
var lr = ss.getLastRow();
var templateText = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Template").getRange(1,1).getValue();
//for (var i =2;i<=lr;i++){
var currentEmail = column(i,1);
var currentClass = column(i,3).getValues();
var currentName = ss.getRange(i,2).getValues();
var messageBody = templateText.replace("{name}",currentName).replace("{class}",currentClass);
var subjectLine = "Reminder:" + currentClass + " Upcoming Class";
MailApp.sendEmail(currentEmail,subjectLine,messageBody);
Issue:
There are many issues in your code, including undefined variables (e.g. column, i), but I think you want to do the following:
Send an email for each row in Sheet1, taking into account that:
recipient is in column A.
subject is based on the value of column C.
The message body is built by replacing the keywords {name} and {class} from a string template found in Template!A1 with the values of column B and C respectively.
Code snippet:
If the above is correct, you can do the following:
function yourFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet1");
var firstRow = 2;
var numRows = sheet1.getLastRow() - firstRow + 1;
var templateText = ss.getSheetByName("Template").getRange(1,1).getValue();
var values = sheet1.getRange(firstRow,1,numRows,3).getValues(); // Retrieve data from all rows
values.forEach(row => { // Iterate through each row
var [currentEmail, currentName, currentClass] = row; // Get values from columns A-C
var messageBody = templateText.replace("{name}",currentName)
.replace("{class}",currentClass);
var subjectLine = "Reminder:" + currentClass + " Upcoming Class";
MailApp.sendEmail(currentEmail,subjectLine,messageBody);
});
}
Reference:
Tutorial: Sending emails from a Spreadsheet
Best Practices

Google Sheets Script for passing a few cell values on one sheet to new sheet row

I have looked and looked and tried to adapt some scripts but am stuck.
Can someone help/show me how to add a collection of specific cells on one sheet and populate the next available row on a second sheet with these values? Please. This is what I have:
function AddToDB2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var target = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("PurchaseOrder");
var targetSheet = target.getSheetByName("DataBase");
var range = ss.getDataRange();
var values = range.getValues();
var date1 = values[1][6]; //these values are collected from multiple cells not in a specific row or col
var po = values[2][6];
var vendor = values[7][0];
var shipto = values[7][3];
//this was for testing that the values were grabbed correctly.
var data = [date1,po,vendor,shipto];
// I don't think this part is even close
var lastRow = targetSheet.getLastRow()
var rowData = values[0]
var newData = []
for(n=1;n<lastRow;++n){newData.push(rowData)}
targetSheet.getRange("C2:H"+lastRow).setValues(newData);
}
This is the other way I came up with:
function addToDB() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var target = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("PurchaseOrder");
var targetSheet = target.getSheetByName("DataBase");
var sourceRangePO = sourceSheet.getRange("G3");
var poValues = sourceRangePO.getValues();
if(poValues != "") {
var sourceRangeDate = sourceSheet.getRange("G2");
var targetRangeDate = targetSheet.getRange("A2");
var dateValues = sourceRangeDate.getValues();
targetRangeDate.setValues(dateValues);
var sourceRangePO = sourceSheet.getRange("G3");
var targetRangePO = targetSheet.getRange("B2");
var poValues = sourceRangePO.getValues();
targetRangePO.setValues(poValues);
var sourceRangeVendor = sourceSheet.getRange("A8");
var targetRangeVendor = targetSheet.getRange("C2");
var vendorValues = sourceRangeVendor.getValues();
targetRangeVendor.setValues(vendorValues);
var sourceRangeShipTo = sourceSheet.getRange("D8");
var targetRangeShipTo = targetSheet.getRange("D2");
var shipToValues = sourceRangeShipTo.getValues();
targetRangeShipTo.setValues(shipToValues);
targetSheet.insertRowAfter(1);
Browser.msgBox("Your PO has beed saved!");
}
else { Browser.msgBox("Please enter a PO number"); }
}
You were very close. Try this. I was a little confused by your setValues C:H. You only have 4 values to set. I set them in D-G. You can adjust as needed.
function AddToDB2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("PurchaseOrder");
var targetSheet = ss.getSheetByName("DataBase");
var range = sourceSheet.getDataRange();
var values = range.getValues();
var date1 = values[1][6]; //these values are collected from multiple cells not in a specific row or col
var po = values[2][6];
var vendor = values[7][0];
var shipto = values[7][3];
var data = [date1,po,vendor,shipto];
var lastRow = targetSheet.getLastRow()
var newData = []
for(n=0;n<data.length;++n){newData.push([data[n]])}
var rng =targetSheet.getRange(lastRow+1,1,1,4) // Col A-D
rng.setValues([newData]);
}

Use an if statement in Javascript to determine if a cell in a google spreadsheet has contents

This code gets values from different locations in my sheet and writes the combined data to another sheet.
Problem is, if the row is blank, the code copies a blank row into the receiving sheet. I would like to first check cell "B8", and if it is blank, skip this function and run the next function.
If cell "B8" has data in it (anything, but it will usually be a date), then I would like to run the function to copy the code to the new sheet.
Here is my code:
function submitButtonClick() {
//Begining of entry8 transfer start.
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
Logger.log('sheet.getName(): ' + sheet.getName());
if (sheet.getName() !== "TimeCardEntry") {return;};
var targetSheet = ss.getSheetByName("TimeSheetRecord");
var arrayOfData = [];
var weekBegining = sheet.getRange(4, 11).getValue();
var employName = sheet.getRange(4, 4).getValue();
var entry8 = sheet.getRange(8,2,1,9);
var entry8Data = entry8.getValues()[0];//Get inner array of the rows data
Logger.log('entry8Data: ' + entry8Data);
//Add weekBegining and employName values to the array
entry8Data.splice(0, 0, weekBegining, employName);
//Push row data into another, outer array. setValuies() needs a 2D array.
arrayOfData.push(entry8Data);
Logger.log(arrayOfData);
var lastRow = targetSheet.getLastRow();
Logger.log('lastRow: ' + lastRow);
targetSheet.getRange(lastRow+1, 1, 1, entry8Data.length).setValues(arrayOfData);
sheet.getRange('B8:M8').clearContent();
//End of entry8 transfer.
}
You could add some code in between these line:
var arrayOfData = [];
var weekBegining = sheet.getRange(4, 11).getValue();
Change to:
var arrayOfData = [];
var B8_Value = sheet.getRange(8, 2).getValue();
if (B8_Value === "") {return;};//If cell value is empty stop code here
var weekBegining = sheet.getRange(4, 11).getValue();

Use RegEx to replace tags in document with column data from spreadsheet

I've been searching for the answer to this question but have so far been unable to piece together the answer. Please explain any answer you have in really simple terms as I'm fairly new to GAS and RegEx. I've got most of the syntax down but the execution of it in GAS is giving me a hard time.
Basically, I want to write a script that, when the spreadsheet is edited, checks which rows have yet to be merged. Then, on those rows, creates a copy of a template Google Doc and names the document based on the spreadsheet data. From there (this is the hard part), I need it to replace merge tags in the template with the data from the spreadsheet.
The tags in the templates I'll be using look like this: <<mergeTag>>
My idea was to match the whole tag, and replace it with data from the spreadsheet that exists in the column with the same name as what's inside the "<<>>". Ex: <<FooBar>> would be replaced with the data from the column named FooBar. It would obviously be from the current row that needs the merging.
After that, all that's left is to send an email (a few more row-specific personalization) with that document attached (sometimes as a PDF) with the body of the message coming from an HTML file elsewhere in the project.
This is the whole thing I have so far (notice the placeholders here and there that I can personalize for each spreadsheet I use this for):
function onEdit() {
//SPREADSHEET GLOBAL VARIABLES
var ss = SpreadsheetApp.getActiveSpreadsheet();
//get only the merge sheet
var sheet = ss.getSheetByName("Merge Data");
//get all values for later reference
var range = sheet.getActiveRange();
var values = range.getValues();
var lastRow = range.getLastRow();
var lastColumn = range.getLastColumn();
//get merge checker ranges
var urlColumn = range.getLastColumn();
var checkColumn = (urlColumn - 1);
var checkRow = range.getLastRow();
var checkRange = sheet.getRange(2, checkColumn, checkRow);
var check = checkRange.getBackgrounds();
//get template determination range (unique to each project)
var tempConditionRange = sheet.getRange(row, column);
var tempConditionCheck = tempConditionRange.getValues();
//set color variables for status cell
var red = "#FF0000";
var yellow = "#FFCC00";
var green = "#33CC33";
//////////////////////////////////////////////////////////
//DOC GLOBAL VARIABLES
var docTemplate1 = DriveApp.getFileById(id);
var docTemplate2 = DriveApp.getFileById(id);
var docTemplate3 = DriveApp.getFileById(id);
var folderDestination = DriveApp.getFolderById(id);
//////////////////////////////////////////////////////////
//EMAIL GLOBAL VARIABLES
var emailTag = ss.getRangeByName("Merge Data!EmailTag");
var personalizers = "";
var subject = "" + personalizers;
var emailBody = HtmlService.createHtmlOutputFromFile("Email Template");
//////////////////////////////////////////////////////////
// MERGE CODE
for (i = 0; i < check.length; i++) {
//for rows with data, check if they have already been merged
if (check[i] == green) {
continue;
} else {
var statusCell = sheet.getRange((i+2), checkColumn, 1, 1);
var urlCell = sheet.getRange((i+2), urlColumn, 1, 1);
var dataRow = sheet.getRange((i+2), 1, lastRow, (lastColumn - 2))
statusCell.setBackground(red);
//for rows with data, but not yet merged, perform the merge code
//////////////////////////////////////////////////////////
//DOC CREATION
//Determine which template to use
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate1;
}
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate2;
}
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate3;
}
//Create a copy of the template
//Rename the document using data from specific columns, at specific rows
//Move the doc to the correct folder
var docName = "";
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = docCopy.getUrl();
var docToSend = DriveApp.getFileById(docId);
var docBody = DocumentApp.openById(docId).getBody();
Here's where I need the help
//Locate the Merge Tags
//Match Merge Tags to the column headers of the same name
//Replace the Merge Tags with the data from the matched column, from the correct row
function tagReplace() {
var tagMatch = "/(<{2}(\w+)>{2})/g";
}
statusCell.setBackground(yellow);
urlCell.setValue(docURL);
The rest is just finishing up the process
//////////////////////////////////////////////////////////
//EMAIL CREATION
//Create an email using an HTML template
//Use Merge Tags to personalize email
//Attach the doc we created to the email
//Send email to recipients based on data in the sheet
MailApp.sendEmail(emailTag, subject, emailBody, {
name: "Person McPerson",
attachments: [docToSend], //[docToSend.getAs(MIME.PDF)],
html: emailBody,
});
//////////////////////////////////////////////////////////
//CHECK ROW UPDATE
statusCell.setBackground(green);
}
}
}
My sheets all have a frozen first row that acts as the header row. All my columns will be consistently named the exact same thing as the tags (minus the <<>>).
How do I match the tags to the data?
EDIT
```````````````````
The solution did not work as described when I inserted it into my code as follows:
function formMerge() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Merge Data");
var urlColumn = sheet.getMaxColumns();
var checkColumn = urlColumn - 1;
var lastRow = ss.getSheetByName("Form Responses").getLastRow();
var values = sheet.getDataRange().getValues();
var headers = values[0];
var urlRange = sheet.getRange(2, urlColumn, lastRow);
var checkRange = sheet.getRange(2, checkColumn, lastRow);
var check = checkRange.getBackgrounds();
var red = "#ff0404";
var yellow = "#ffec0a";
var green = "#3bec3b";
var docTemplate = DriveApp.getFileById(id);
var folderDestination = DriveApp.getFolderById(id);
// MERGE CODE
for (i = 0; i < check.length; i++) {
if (check[i] == green) {
continue;
} else {
var statusCell = sheet.getRange((i+2), checkColumn, 1, 1);
var urlCell = sheet.getRange((i+2), urlColumn, 1, 1);
var dataRow = sheet.getRange((i+2), 1, 1, (urlColumn - 2)).getValues();
var clientNameRange = sheet.getRange((i+2), 3);
var clientName = clientNameRange.getValue();
var dateRange = sheet.getRange((i+2), 2);
var datePreFormat = dateRange.getValue();
var timeZone = CalendarApp.getTimeZone();
var date = Utilities.formatDate(new Date(datePreFormat), timeZone, "MM/dd/yyyy");
statusCell.setBackground(red);
//EMAIL VARIABLES
var personalizers = clientName;
var subject = "Post Intake Report for " + personalizers;
var emailBody = "Please see the attached Google Doc for the Post Intake Report for " + clientName + ". The intake was performed on " + date + ".";
var emailTagRange = sheet.getRange((i+2), 24);
var emailTagValue = emailTagRange.getValue();
var emailTag = emailTagValue.split(", ");
//DOC CREATION
var docToUse = docTemplate;
var docName = "Post Intake Report - " + clientName + " [" + date + "]";
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = docCopy.getUrl();
var docBody = DocumentApp.openById(docId).getBody().editAsText();
for (var j=0; j<headers.length; j++) {
var re = new RegExp("(<<"+headers[j]+">>)","g");
docBody.replaceText(re, dataRow[j]);
}
statusCell.setBackground(yellow);
urlCell.setValue(docURL);
//EMAIL CREATION
MailApp.sendEmail(emailTag, subject, emailBody, {
name: "Christopher Anderson",
attachments: [docCopy],
html: emailBody
});
statusCell.setBackground(green);
}
}
}
Build the RegExp for each tag on the fly, using the header values from your spreadsheet.
Use Body.replaceText() to perform the replacements.
var values = sheet.getDataRange().getValues();
var headers = values[0];
...
// Loop over all columns. Use header names to search for tags.
for (var col=0; col<headers.length; col++) {
// Build RegExp using column header
var re = new RegExp("(<{2}"+headers[col]+">{2})","g");
// Replace tags with data from this column in dataRow
body.replaceText(re, dataRow[col]);
}
This snippet will operate on a single row; the first couple of declarations should appear outside of your row loop. The column looping is then done after you've created and opened the new document, and obtained the body object.
It loops over all the columns in the spreadsheet, using the header names to find the tags you've defined, and replaces them with the corresponding cell contents for the current row.

Google App Script - onFormSubmit - copyTo cell in spreadsheet

I have a script that copies a certain cell when a new form is submitted, (which contains a drop down list, on sheet "Validation") and pastes it into the first blank cell in column A on the sheet "Internal Recruitment Requests".
The code is not copying into the right cell.
Below is the current script, which doesn't work.
I am running it with onEdit at the moment for testing purposes.
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Validation');
var sheet2 = ss.getSheetByName('Internal Recruitment Requests');
var lastEmptyOnColumnA = sheet2.getRange("A1:A" + sheet2.getLastRow()).getValues().join(",").replace(/,,/g, '').split(",").length;
var DVcopyFrom = sheet1.getRange("A4");
var DVcopyTo = sheet2.getRange('A' + lastEmptyOnColumnA);
sheet1.getRange(DVcopyFrom).copyToRange(sheet2.getRange(DVcopyTo), {contentsOnly:false});
}
Bingo, all sorted. used the below script which was a tiny tweak on what you had given me. thanks for the help :)
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Validation');
var sheet2 = ss.getSheetByName('Internal Recruitment Requests');
var lastRowSheet2 = sheet2.getLastRow();
var cellRefLastRowSheet2 = "A1:A" + lastRowSheet2.toString();
var cellRefLastRowSheet2Empty = "A" + lastRowSheet2.toString();
var lastEmptyOnColumn_A_Sheet2 = sheet2.getRange(cellRefLastRowSheet2);
var DVcopyFromValue = sheet1.getRange("A4");
var contentOfLastCell = sheet2.getRange(cellRefLastRowSheet2Empty).getValue();
if (contentOfLastCell === "")
{
DVcopyFromValue.copyTo(sheet2.getRange(cellRefLastRowSheet2Empty));
}
}

Categories