How to loop command using the .getRowIndex() in Google Sheets? - google-apps-script

function getColIndexByName(colName) {
var sheet = SpreadsheetApp.getActiveSheet();
var numColumns = sheet.getLastColumn();
var row = sheet.getRange(1, 1, 1, numColumns).getValues();
for (i in row[0]) {
var name = row[0][i];
if (name == colName) {
return parseInt(i) + 1;
}
}
return -1;
}
function sendtoContacts () {
var source = SpreadsheetApp.getActiveSpreadsheet();
var ss = source.getActiveSheet();
var row = ss.getActiveRange().getRowIndex();
var group = ContactsApp.getContactGroup('From Googlesheet');
var givenName = ss.getRange(row, getColIndexByName("First")).getValue();
var familyName = ss.getRange(row, getColIndexByName("Last")).getValue();
var email = ss.getRange(row, getColIndexByName("Work Gmail")).getValue();
var Homeemail = ss.getRange(row, getColIndexByName("Personal Email")).getValue();
var company = ss.getRange(row, getColIndexByName("Company")).getValue();
var title = ss.getRange(row, getColIndexByName("Title")).getValue();
var phone = ss.getRange(row, getColIndexByName("Phone")).getValue();
var mobile = ss.getRange(row, getColIndexByName("Mobile")).getValue();
var newContact = ContactsApp.createContact(givenName, familyName, email);
var contactid = newContact.getId();
var addy = ss.getRange(row, getColIndexByName("Address")).getValue();
var city = ss.getRange(row, getColIndexByName("City")).getValue();
var prov = ss.getRange(row, getColIndexByName("Prov")).getValue();
var pc = ss.getRange(row, getColIndexByName("Postal Code")).getValue();
var address = addy + ", " + city + ", " + prov + ", " + pc
var AltContact = ss.getRange(row, getColIndexByName("Alt Contact Name")).getValue();
var AltRelation = ss.getRange(row, getColIndexByName("Alt ContactRelation")).getValue();
var AltPhone = ss.getRange(row, getColIndexByName("Alt Contact Phone")).getValue();
var AltWork = ss.getRange(row, getColIndexByName("Alt Contact Wk No")).getValue();
var AltMobile = ss.getRange(row, getColIndexByName("Alt Contact Mobile")).getValue();
newContact.addToGroup(group);
newContact.addAddress("Home", address);
newContact.addCompany(company, title);
newContact.addEmail("Home", Homeemail);
newContact.addCustomField("Emergency Contact", AltContact);
newContact.addCustomField("Emergency Contact Relation", AltRelation);
newContact.addCustomField("Emergency Contact Work", AltWork);
newContact.addCustomField("Emergency Contact Mobile", AltMobile);
for ( var i = 0; i < phone.length ; i++){
if (phone[i][3] != ''){ newContact.addPhone("HOME", phone); return}};
for ( var i = 0; i < mobile.length ; i++){
if (mobile[i][44] != ''){ newContact.addPhone("Mobile", mobile); return}};
}
function MakeAllContacts() {
var source = SpreadsheetApp.getActiveSpreadsheet();
var ss = source.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 100; // Number of rows to process
for (row = 2; row < 6; row++)
{
sendtoContacts();
}
return
}
Here I am duplicating the entries with MakeAllContacts() but I want to make the RowIndex change to every row in the sheet so it add all the contacts in the sheet. Here is at Video I made explaining it Video and here is a link to my actual sheet Google Sheet. I have a fair bit of code I would like to start sharing if I could just get my head are around looping instead of the one row to be All the rows in the sheet. Thanks for any help is appreciated.

Your sendtoContacts () function is using ss.getActiveRange().getRowIndex(); to determine which row to use but nowhere in your script you set any row to active so you keep using the same data all the way through the main loop in MakeAllContacts().
There are 2 possible solutions :
use activate() in the MakeAllContacts() function loop so that the active row changes for each iteration (ss.getRange(row,1).activate())
use a rowIndex parameter in the sendtoContacts () function like below :
function MakeAllContacts() {
var source = SpreadsheetApp.getActiveSpreadsheet();
var ss = source.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 100; // Number of rows to process
for (row = 2; row < numRows; row++){
sendtoContacts(row);
}
}
and then change the function sendtoContacts () function like this:
function sendtoContacts (row) { // row as parameter
var source = SpreadsheetApp.getActiveSpreadsheet();
var ss = source.getActiveSheet();
...
That said, this approach is not very efficient since each data is read in the spreadsheet using a single getRange/getValue which is particularly slow... please read the best practice to get inspiration on how to handle your data more efficiently using a single getValues() and iterate the array content instead.

Related

Copy Row from one Sheet to another sheet which is linked in the row

I have previously been able to copy rows from one Spreadsheet to another, however what I am trying to do now is copy a row to a specific spreadsheet that is linked on the same row and I can't seem to work it out.
I have around 500 rows of names with data (I cannot share a sheet as my firms Google does not allow for sharing externally but I have a screenshot of dummy data below). I used a formula to find the Unique names and then created a Google Sheet for each person and linked the Sheet back to the master data.
Master Data Sheet (tab is called Sheet1)
I am looking for a script that will work through the sheet, copying the rows to the link that is on the same row but I just can't figure it out - the following script does not work but I am at a loss so any help would be appreciated.
Apologies if I haven't explained myself very well!!
function copyTo(){
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = sSheet.getSheetByName("Sheet1");
var data = srcSheet.getDataRange().getValues();
for(var i = 1; i < data.length; i++) {
var row = data[i];
var link = row[3];
var id = row[4];
var complete = row[5];
var COMPLETE = "Complete"
if(link !== "" && complete !== "Complete"){
var srcRange = srcSheet.getRange("A" + i + ":C" + i);
var linkID = id.toString()
var tarSS = DriveApp.getFileById(linkID);
var tarSheet = tarSS.getSheetbyName("Sheet1");
var tarRow = tarSheet.getLastRow();
//tarSheet.insertRowAfter(tarRow);
var tarRange = tarSheet.getRange("A" + (tarRow+1) + ":C" + (tarRow+1));
srcRange.copyTo(tarRange);
srcSheet.getRange(i+1,6).setValue(COMPLETE)
}
}};
Try this:
function copyTo() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("Sheet1");
var vs = sh.getDataRange().getValues();
for (var i = 1; i < vs.length; i++) {
var row = vs[i];
var link = row[3];
var id = row[4];
var complete = row[5];
var COMPLETE = "Complete"
if (link !== "" && complete !== "Complete") {
var rg = sh.getRange("A" + i + ":C" + i);
var linkID = id.toString()
var tarSS = SpreadsheetApp.openById(linkID);
var tarSheet = tarSS.getSheetbyName("Sheet1");
var tarRow = tarSheet.getLastRow();
var tarRange = tarSheet.getRange("A" + (tarRow + 1) + ":C" + (tarRow + 1));
rg.copyTo(tarRange);
sh.getRange(i + 1, 6).setValue(COMPLETE)
}
}
};
As much as I understand you question here is the script which will work for you exactly you want .
This will copy data from sheet you want and then create a new spreadsheet and paste into that sheet then link will show of this in same sheet from data copied
Let me know how this worked .
Code.gs
const sendDataToSheet = () => {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const ws = ss.getSheetByName(''paste here your sheet name'')
const newSheet = SpreadsheetApp.create("give new sheet name here")
ws.copyTo(newSheet).setName("Copied Data")
const sheetURL = newSheet.getUrl()
ws.insertRowBefore(1)
ws.getRange(1,1).setValue(sheetURL)
}

Google Apps Script : Get row number matching value

I'm need some assistance in my problem.
I'm currently working on a script that works like this :
Send form
Lookup the value in table
Get the row number to use in another function
I have searched for the solution but mostly use logger to display value, I need the value as local variable to be used in other function.
Here is my current code up until the point I try to get row number :
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var Timestamp = e.values[0];
var Nama = e.values[1];
var Prinsipal = e.values[2];
var Email = e.values[3];
var Site = e.values[4];
var Mobil = e.values[5];
var Jumlah = e.values[6];
var ETD = e.values[7];
var ETA = e.values[8];
var Keterangan = e.values[9];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Master Email Site');
var data = sheet.getDataRange().getValues();
var RowCabang = Site
for(var i = 0; i<data.length;i++){
if(data[i][0] == Site){ //[0] because column A
return i+1;
}
}
var EmailCol = 2;
var EmailDest = sheet.getRange(RowCabang, EmailCol).getValue();
With this script, the trigger is completed but it doesnt capture the row number as var to be used.
Thank you for your assistance.
Try this pattern:
let RowCabang;
data.some((row, index) => {
if (row[0] === Site) {
RowCabang = index + 1;
return true;
}
});

Add the time when one google sheets document is modified to another sheets document

I have one sheet for a group of people. I'm trying to make a script so that whenever they edit the sheet, the time that they last edited will show up in a sheet in another document.
A sample row from the group sheet looks like this:
col1|...|col10|person name|...|col17
and the sheet that the time should be going to looks like this:
person name|col2|...|col10|date last edited.
this is the code that I've been trying and it is not working so far:
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColumn();
var time = new Date().toLocaleString("en-US", {timeZone: "America/Toronto"})
var ID = "sheet ID"; //The ID for the second sheet goes here
var sheet = SpreadsheetApp.OpenById(ID);
var target = sheet.getSheetByName("Sheet2");
var lastColumn = target.getLastColumn();
var lastRow = target.getLastRow();
var range = target.getRange(1,1,lastRow, lastColumn);
var data = range.getValues();
var userEmail = e.user.getEmail();
var array = userEmail.split(".");
var firstName = array[0];
var secondSheet = e.source.getActiveSheet();
var lastRowSecond = secondSheet.getLastRow();
var lastColumnSecond = secondSheet.getLastColumn();
var secondRange = secondSheet.getRange(1,1,lastRowSecond, lastColumnSecond);
var secondData = secondRange.getValues();
var nameRow = secondData[row-1];
var nameColumn = nameRow[10];
var secondPersonarray = nameColumn.split(" ");
var secondPersonFirstName = secondPersonarray[0];
for (var i=1; i<lastRow; i++) {
var sheetRow = data[i];
var name = sheetRow[0].split(" ");
var firstNameSheet = name[0];
if ((firstName == firstNameSheet) && (firstName == secondPersonFirstName)) {
target.getRange(sheetRow, 10).setValue(time);
}
}
}
I've done something similar, only with one document, so I know the problem isn't with the time.

How to create a new document from a template with placeholders

I'm trying to create a script that will create new documents from a template-document. Replace placeholders in the documents with data from the sheet based on a keyword search in a specific column. And then change the row's value in the specific column so that the row will not process when the script runs again.
I think I've got it right with the first keyword search, and the loop through the rows. But the last part to get the data to 'merge' to the placeholders I can't figure out how to. I just get the value "object Object" and other values in the document.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var lastColumn = s.getLastColumn();
function createDocFromSheet() {
var headers = getUpsertHeaders(s);//function is defined outside of this function
var statusColNum = headers.indexOf('Status')+1;
var row = getRowsData(s); //The function is defined outside this function.
for (var i=0; i<row.length; i++) {
var jobStatus = '';
if (row[i]['Status'] === '') {
//New: write the status to the correct row and column - this will be moved to the end when I get the rest right
var jobStatus = "Created";
s.getRange(i+2, statusColNum).setValue(jobStatus);
//Find the template and make a copy. Activate the body of the new file.
var templateFile = DriveApp.getFileById('1lkfmqsJMjjPujHqDqKtcDmL-5GMIxpOWTyCOaK29d2A');
var copyFile = templateFile.makeCopy()
var copyId = copyFile.getId()
var copyDoc = DocumentApp.openById(copyId)
var copyBody = copyDoc.getActiveSection()
//Find the rows Values as an object.
var rows = s.getRange(i+2,1,1,lastColumn)
var rowsValues = rows.getValues();
Logger.log(rowsValues)
//Until here I think it's okay but the last part?
//HOW TO replace the text???
for (var columnIndex = 0; columnIndex < lastColumn; columnIndex++) {
var headerValue = headerRow[columnIndex]
var rowValues = s.getActiveRange(i,columnIndex).getValues()
var activeCell = rowsValues[columnIndex]
//var activeCell = formatCell(activeCell);
Logger.log(columnIndex);
copyBody.replaceText('<<' + headerValue + '>>', activeCell)
}
Template doc : Link
Template sheet: Link
You can use the following GAS code to accomplish your goals:
var DESTINATION_FOLDER_ID = 'YOUR_DESTINATION_FOLDER_ID';
var TEMPLATE_FILE_ID = 'YOUR_TEMPLATE_FILE_ID';
function fillTemplates() {
var sheet = SpreadsheetApp.getActiveSheet();
var templateFile = DriveApp.getFileById(TEMPLATE_FILE_ID);
var values = sheet.getDataRange().getDisplayValues();
var destinationFolder = DriveApp.getFolderById(DESTINATION_FOLDER_ID);
for (var i=1; i<values.length; i++) {
var rowElements = values[i].length;
var fileStatus = values[i][rowElements-1];
if (fileStatus == 'Created') continue;
var fileName = values[i][0];
var newFile = templateFile.makeCopy(fileName, destinationFolder);
var fileToEdit = DocumentApp.openById(newFile.getId());
for (var j=1; j<rowElements-1; j++) {
var header = values[0][j];
var docBody = fileToEdit.getBody();
var patternToFind = Utilities.formatString('<<%s>>', header);
docBody.replaceText(patternToFind, values[i][j]);
}
sheet.getRange(i+1, rowElements).setValue('Created');
}
}
You only have to replace the 1st and 2nd lines as appropriate. Please do consider as well that the code will assume that the first column is the file name, and the last one the status. You can insert as many columns as you wish in between.
After some coding I ended up with this code to process everything automatic.
Again thanks to #carlesgg97.
The only thing I simply can't figure out now is how to generate the emailbody from the template with dynamic placeholders like in the document. How to generate the var patternToFind - but for the emailbody?
I've tried a for(var.... like in the document but the output doesn't replace the placeholders.
var DESTINATION_FOLDER_ID = '1inwFQPmUu1ekGGSB5OnWLc_8Ac80igK0';
var TEMPLATE_FILE_ID = '1lkfmqsJMjjPujHqDqKtcDmL-5GMIxpOWTyCOaK29d2A';
function fillTemplates() {
//Sheet variables
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
var values = sheet.getDataRange().getDisplayValues();
//Header variables
var headers = sheet.getDataRange().getValues().shift();
var idIndex = headers.indexOf('ID');
var nameIndex = headers.indexOf('Name');
var emailIndex = headers.indexOf('Email');
var subjectIndex = headers.indexOf('Subject');
var statusIndex = headers.indexOf('Status');
var fileNameIndex = headers.indexOf('File Name');
var filerIndex = headers.indexOf('Filer');
var birthIndex = headers.indexOf('Date of birth');
//Logger.log(statusIndex)
//Document Templates ID
var templateFile = DriveApp.getFileById(TEMPLATE_FILE_ID);
//Destination
var destinationFolder = DriveApp.getFolderById(DESTINATION_FOLDER_ID);
var templateTextHtml = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Email').getRange('D11').getValue();
//Run through the variables
for (var i=1; i<values.length; i++) {
//If first column is empty then stop
var index0 = values[i][0];
if(index0 == "") continue;
var rowElements = values[i].length;
var fileStatus = values[i][statusIndex];
//If the row already processed then stop
if (fileStatus == "Created") continue;
//If the row is not processed continue
//Define the new filename by the relevant Column
var fileName = values[i][fileNameIndex];
var newFile = templateFile.makeCopy(fileName, destinationFolder);
var fileToEdit = DocumentApp.openById(newFile.getId());
//Replace placeholders in the new document
for (var j=1; j<rowElements-1; j++) {
var header = values[0][j];
var docBody = fileToEdit.getBody();
var patternToFind = Utilities.formatString('{{%s}}', header);
docBody.replaceText(patternToFind, values[i][j]);
}
//Create the PDF file
fileToEdit.saveAndClose();
var newPdf = DriveApp.createFile(fileToEdit.getAs('application/pdf'));
DriveApp.getFolderById(DESTINATION_FOLDER_ID).addFile(newPdf);
DriveApp.getRootFolder().removeFile(newPdf);
newFile.setTrashed(true);
var newPdfUrl = newPdf.getUrl();
//Create the emailbody
var textBodyHtml = templateTextHtml.replace("{{Name}}",values[i][nameIndex]).replace("{{Date of birth}}",values[i][birthIndex]);
var textBodyPlain = textBodyHtml.replace(/\<br>/mg,"");
//Will send email to email Column
var email = values[i][emailIndex];
var emailSubject = values[i][idIndex]+" - "+values[i][fileNameIndex]+" - "+values[i][nameIndex];
MailApp.sendEmail(email,emailSubject,textBodyPlain,
{
htmlBody: textBodyHtml+
"<p>Automatic generated email</p>",
attachments: [newPdf],
});
sheet.getRange(i+1, filerIndex+1).setValue(newPdfUrl);
sheet.getRange(i+1, statusIndex+1).setValue('Created');
}//Close for (var i=1...
}

update google contact with marked sheet using google app scripts

I'm trying to update my Google contacts from a sheet. I want to search column I of the sheet to check for 'pending'. If a row is pending, I want to take the values in column G, H, and E of that row and add it to that contact's Note.
Here's what I've got so far. Currently stuck on how to get the row of a contact marked 'pending'.
function test(){
var ss = SpreadsheetApp.getActiveSheet();
var data = ss.getDataRange().getValues(); // read all data in the sheet
for(n=9;n<data.length;++n){ // iterate row by row and examine data in column I
if(data[n][0].toString().match('pending')){
var row = ???
var name = ss.getRange(row, 1).getValue();
var contacts = ContactsApp.getContactsByName(name);
for (var i in contacts) {
var donate = ss.getRange(row, 7).getValue();
var pickup = ss.getRange(row, 8).getValue();
var item = ss.getRange(row, 5).getValue();
contacts[i].setNotes(donate + '\n\n' + item + '\n\n' + pickup + '\n\n');}
ss.getRange('9**row**').setValue('done');
}
}
}
This should do, haven't tested it though. Let me know if the code does not work.
function test()
{
var ss = SpreadsheetApp.openById(<SPREADSHEET_ID>).getSheetByName("Sheet1");
var lastRow = ss.getLastRow();
var data = ss.getRange("I1:I"+lastRow).getValues(); // read all data in the sheet
for(var n=0;n<data.length;n++)
{ // iterate row by row and examine data in column I
if(data[n] == 'pending')
{
//var row = ???
var name = ss.getRange("A"+n).getValue();
var contacts = ContactsApp.getContactsByName(name);
for (var i in contacts)
{
var donate = ss.getRange("G"+n).getValue();
var pickup = ss.getRange("H"+n).getValue();
var item = ss.getRange("E"+n).getValue();
contacts[i].setNotes(donate + '\n\n' + item + '\n\n' + pickup + '\n\n');}
ss.getRange('9**row**').setValue('done');
}
}
}
Instead of using var ss = SpreadsheetApp.getActiveSheet(); you should consider using var ss = SpreadsheetApp.openSheetById(<SPREADSHEET_ID>).getSheetByName(<SHEET_NAME>);