This is just a snippet of my code from Google App Script which iterates through each row in columns 1, 2, 3. If an edit is made in column 3, an incremental ID will be generated and a concatenation of the same row and different columns will also be generated - in this case Column D, E, and F. I am struggling with figuring out a way to change the formulas into values. What am I missing here?
// Location format = [sheet, ID Column, ID Column Row Start, Edit Column]
var locations = [
["Consolidated Media Plan",1,9,3]
];
function onEdit(e){
// Set a comment on the edited cell to indicate when it was changed.
//Entry data
var range = e.range;
var col = range.getColumn();
var row = range.getRow();
// Location Data
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
function getNewID(){
function IDrange(){
var dataRange = sheet.getDataRange();
var lastRow = dataRange.getLastRow();
return sheet.getRange(IDrowStart,IDcol,lastRow-IDrowStart).getValues();
};
//Get largest Value in range
function getLastID(range){
var sorted = range.sort();
var lastIDval = sorted[sorted.length-1][0];
return lastIDval;
};
//Stores leading letters and zeroes and trailing letters
function getLettersNzeroes(id){
//Get any letters or zeroes.
var re = new RegExp("^([a-zA-Z0])$");
var letterZero = [];
for(char = 0; char < id.length; char++){
if(re.test(id[char])){
letterZero.push([char,id[char]]);// [[position, letter or zero]]
};
};
// Categorize letters and zeroes into start and end blocks
var startLetterZero = "",
endLetter = "",
len = letterZero.length - 1;
for(j = 0; j < letterZero.length; j++){
if(letterZero[j][0] === j){
startLetterZero += letterZero[j][1];
}else if(letterZero[j][1] !== "0" && letterZero[len][0] - (len - j) == letterZero[j][0]){
endLetter += letterZero[j][1];
};
};
var startNend = {"start":startLetterZero,"end":endLetter};
return startNend;
};
//Gets last id number. Adds 1 an checks to set if its new length is greater than the lastNumber.
function getNewNumber(id){
var removeZero = false;
var lastNum = parseInt(id.replace(/\D/g,''),10);//Remove letters
var newNum = (lastNum+1).toString();
if(lastNum.toString().length !== newNum.length){
var removeZero = true;
};
var newNumSet = {"num":newNum, "removeZero": removeZero};
return newNumSet
};
var lastID = getLastID(IDrange());
var lettersNzeroes = getLettersNzeroes(lastID);
var newNumber = getNewNumber(lastID);
//If the number is 9,99,999,9999 etc we need to remove a zero if it exists.
if(newNumber.removeZero === true && lettersNzeroes.start.indexOf("0") !== -1.0){
lettersNzeroes.start = lettersNzeroes.start.slice(0,-1);
};
//Rejoin everything together
var newID = lettersNzeroes.start +
newNumber.num +
lettersNzeroes.end;
return newID;
};
for(i = 0; i < locations.length; i++){
var sheetID = locations[i][0],
IDcol = locations[i][1],
IDrowStart = locations[i][2],
EditCol = locations[i][3];
var offset = IDcol - EditCol;
var cell = sheet.getActiveCell();
if(sheetID === sheet.getName()){
if(EditCol === col){
//ID Already Exists the editing cell isn't blank.
if(cell.offset(0,offset).isBlank() && cell.isBlank() === false){
var newID = getNewID();
cell.offset(0,offset).setValue(newID);
cell.offset(0,-1).setFormulaR1C1('=concatenate(R[0]C[-1],"_",INDEX(Glossary!K:K,MATCH(R[0]C[2],Glossary!J:J,0)))');
};
};
};
};
};
EDIT:
This is my full code, I have been unsuccessful with trying to retrieve just the values of the formula within the same (i.e, If C9 gets edited, a formula with the values specific to the 9th row should be populated)
Also, I've tried to add an index/match formula to the concatenation formula at the bottom of the code - it works as expected on the google sheets, but when I run it with the script it pastes the correct formula but it returns a #NAME? error message. However, when I copy and paste the exact same formula in the cell, it works perfectly, any idea what could be causing this error?
This works for me. I know it's not exactly the same thing but I didn't have access to getNewId()
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()!='Sheet1')return;
//e.source.toast('flag1');
if(e.range.columnStart==3 && e.range.offset(0,1).isBlank() && e.value) {
//e.source.toast('flag2');
e.range.offset(0,1).setValue(e.value);
e.range.offset(0,2).setFormulaR1C1('=concatenate(R[0]C[-1],"_",R[0]C[-2],"_",R[0]C[-3],"_",R[0]C[-4])');
}
}
I have a rota (a fixed order of rotation (as of persons or duties)) that I've already had help with this week. It's up & running as is, but for simpler reading I'd like to transpose it.
You can see the transposed sheet as I'd like it here
The current script is for the pre-transposed table.
It would search Column 0 for the date. If it was was 7 days away it would retrieve the name from Column 1 & match it with e-mail address in separate sheet etc.
What I'd like to do is instead have the Date in Row 0 & then subsequent names in Row 1 etc etc
I've tried various things. I've stepped through the code & can see what it's doing, & I've done some reading through about 2 dimensional arrays, but I can't seem to find a way of getting the code to work down through columns, instead of across the rows.
Here's the code:
function sendEmails() {
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss1.getSheetByName("Rota")
ss1.setActiveSheet(sh1);
var rotalink = "https://docs.google.com/spreadsheets/d/1LgzUWSAGA2kbpar8r5nosU1bSHF7nrtvtUiHS3nB_e8";
var sheet = SpreadsheetApp.getActiveSheet();
// Fetch the range
var dataRange = sheet.getRange("B3:G50")
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var today=new Date();
var timecell = new Date(row[0]);
var timediff = new Date();
var one_day=1000*60*60*24;
var daystogo = Math.ceil((timecell.getTime()-today.getTime())/(one_day));
if (daystogo==7) {//only e-mail people with one week to go. To change that alter the "7" to the number of days you want
var subject = "Rota reminder!";
var emailAddress = [];
var message;
message = "Hello \n\n"+
"You are down to help at Youth Café this week. \n\n" +
"Please see the below rota for your role \n\n" +
"If you have any questions or problems let us know at thameyouthcafe#gmail.com \n\n" +
"Remember, you can check the rota anytime by clicking on the link below: \n\n"+
rotalink
for (var x = 1; x < 5; x++) { // 5 because emails are till col4
// var emailAddress = []; // Start by collecting the non-blank emails in an array
if (getEmailFromName(row[x]) != "") {
emailAddress.push(getEmailFromName(row[x]))
}
}
emailAddress = emailAddress.join(); // Join the array to get a comma separated string
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
and here's the getEmailFromName function that matches with SKey (which I presume comes from the "i" variable in the first function?
function getEmailFromName(sKey) {
// to use this function, don’t put anything in the first column (A) or row (1).
// Put the name (i.e. the key, or what we’re looking for) in column B.
// Put what we want to return in column C.
var columnToSearch = 1; //column B
// Set the active sheet to our email lookup
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss1.getSheetByName("EmailContactList")
ss1.setActiveSheet(sh1);
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var line = -1;
for( var i = 0; i < data.length; i++ ) {
if( data[i][columnToSearch] == sKey ) {
line = i;
break;
}
}
if( line != -1 ) {
//do what you want with the data on "line"
return data[line][2]; //value on column C of the matched line
}
else {
return "";
// if criteria is not found
}
}
Try it this way:
function sendEmails() {
var ss1 = SpreadsheetApp.getActive();
var sh1 = ss1.getSheetByName("Rota")
ss1.setActiveSheet(sh1);
var rotalink = "https://docs.google.com/spreadsheets/d/1LgzUWSAGA2kbpar8r5nosU1bSHF7nrtvtUiHS3nB_e8";
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange("B3:G50")
var data = dataRange.getValues();
for (var i=0;i<dataRange.length;i++) {
var row = data[i];
var today=new Date();
var timecell = new Date(row[0]);
var timediff = new Date();
var one_day=1000*60*60*24;
var daystogo = Math.ceil((timecell.getTime()-today.getTime())/(one_day));
if (daystogo==7) {
var subject = "Rota reminder!";
var emailAddress = [];
var message = Utilities.formatString('Hello\n\nYou are down to help at Youth Café this week.\n\n Please see the below rota for your role \n\nIf you have any questions or problems let us know at thameyouthcafe#gmail.com \n\nRemember, you can check the rota anytime by clicking on the link below: \n\n%s',rotalink);
for (var x=1;x<5;x++) {
if(data[i][x]) {
emailAddress.push(data[i][x]);
}
}
MailApp.sendEmail(emailAddress.join(), subject, message);
}
}
}
Managed to solve it - thank you for your contributions. Turned out it was incredibly simple.
Just had to change this line:
var timecell = new Date(data[0])
to this:
var timecell = new Date(data[0][i])
so it iterates through the first row of each column.
I'm very new to Javascript but have recently started modifying scripts to let us pull various csv reports into Google Sheets, using Google Apps Script. The current script we have for this reads the csv file and then inserts all data after the 8th row (since the rows before that just contain info such as when it was generated, report name etc.).
The challenge now is that we need a year-to-date report that is split by month into Sheets (if we split by day we reach the 2 million cell limit). But as the reporting tool in this case doesn't allow to segment by any other date variable than day, we have to do the split within the script. So we would basically have a YTD report sent with ad-level data split by day. This ad-level data is then grouped by month rather than day within the script, before the data is inserted into Sheets. In other words, I want e.g. cost, impression and click data for an ad to be summed for all daily occurrences within one month.
I've looked around intensively the last couple of days but haven't found a working solution for this specific problem yet. I would be super grateful if someone wanted to take a look at this.
For your reference, please find two scripts below. The first one is the working script we currently have, which just insert the csv data from the 8th row (but no grouping):
function importData() {
var sheet_atlas = SpreadsheetApp.getActive().getSheetByName('sheet_name');
sheet_atlas.getRange('A2:V10000').clearContent();
var sheetName = "sheet_name"
var threads = GmailApp.search("attachment_name")
var msgs = GmailApp.getMessagesForThreads(threads);
var newData = [];
for (var i = 0 ; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var attachments = msgs[i][j].getAttachments();
var bodyEmail = msgs[0][0].getBody();
var regExp = new RegExp('a href="(.*?)"', "gi");
//var regExp = new RegExp('data-saferedirecturl="(.*?)"', "gi"); // "i" is for case insensitive
var url = regExp.exec(bodyEmail)[1];
Logger.log(url)
var decode = new XML('<d>' + url + '</d>');
var strDecoded = decode.toString()
var response = UrlFetchApp.fetch(strDecoded).getContentText();
var csvdata = Utilities.parseCsv(response)
var newOrders = []
//Logger.log(csvdata)
for (var eachRow in csvdata){
//for(var col1 in csvdata[8]){
//if(csvdata[8][col1] == 'Campaign Name'){ var campaignCol = col1 }
//else if(csvdata[8][col1] == 'Publisher Name'){ var publisherCol = col1 }
//else if(csvdata[8][col1] == 'Statistics Date'){ var dateCol = col1 }
//}
//Logger.log(dateCol)
//Logger.log(publisherCol)
//Logger.log(campaignCol)
if (eachRow>8)
{
var theRow = []
theRow.push(csvdata[eachRow][0])
theRow.push(csvdata[eachRow][1])
theRow.push(csvdata[eachRow][2])
theRow.push(csvdata[eachRow][3])
theRow.push(csvdata[eachRow][4])
theRow.push(csvdata[eachRow][5])
theRow.push(csvdata[eachRow][6])
theRow.push(csvdata[eachRow][7])
theRow.push(csvdata[eachRow][8])
theRow.push(csvdata[eachRow][9])
theRow.push(csvdata[eachRow][10])
theRow.push(csvdata[eachRow][11])
theRow.push(csvdata[eachRow][12])
theRow.push(csvdata[eachRow][13])
theRow.push(csvdata[eachRow][14])
theRow.push(csvdata[eachRow][15])
theRow.push(csvdata[eachRow][16])
theRow.push(csvdata[eachRow][17])
theRow.push(csvdata[eachRow][18])
theRow.push(csvdata[eachRow][19])
theRow.push(csvdata[eachRow][20])
theRow.push(csvdata[eachRow][21])
newOrders.push(theRow)
}
}
}
}
Logger.log(newOrders)
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName).getRange(2,1, newOrders.length, newOrders[0].length).setValues(newOrders)
}
The second one is my current attempt at creating the grouping. So far I've managed to break out the month, but when it comes to the summing part it goes all wrong.
function importData() {
var sheet_atlas = SpreadsheetApp.getActive().getSheetByName('sheet name');
sheet_atlas.getRange('L2:AA60000').clearContent();
var sheetName = "sheet name"
var threads = GmailApp.search("attachment_name")
var msgs = GmailApp.getMessagesForThreads(threads);
var newData = [];
for (var i = 0 ; i < msgs.length; i++) {
for (var j = 0; j < msgs[i].length; j++) {
var attachments = msgs[i][j].getAttachments();
var bodyEmail = msgs[0][0].getBody();
var regExp = new RegExp('a href="(.*?)"', "gi");
var url = regExp.exec(bodyEmail)[1];
Logger.log(url)
var decode = new XML('<d>' + url + '</d>');
var strDecoded = decode.toString()
var response = UrlFetchApp.fetch(strDecoded).getContentText();
var csvdata = Utilities.parseCsv(response)
var newOrders = []
//Logger.log(csvdata)
var currMonth = "";
var theRow = [];
var columns = 11;
var formattedMonth = "";
for (var eachRow in csvdata){
//for(var col1 in csvdata[8]){
//if(csvdata[8][col1] == 'Campaign Name'){ var campaignCol = col1 }
//else if(csvdata[8][col1] == 'Publisher Name'){ var publisherCol = col1 }
//else if(csvdata[8][col1] == 'Statistics Date'){ var dateCol = col1 }
//}
//Logger.log(dateCol)
//Logger.log(publisherCol)
//Logger.log(campaignCol)
if (eachRow>8)
{
var date = csvdata[eachRow][2].split("-")
var month = date[0]
if((currMonth != "") && (month.localeCompare(currMonth) != 0)) {
theRow[2] = formattedMonth
newOrders.push(theRow)
theRow = []
currMonth = month
for (var ci = 0; ci < columns; ci++){
// Reset data structure
theRow.push("0")
}
}
formattedMonth = date[0] + "-" + date[2]
for(var cy = 0; cy < columns; cy++) {
if(cy != 2){
// Sum the columns
theRow[cy] = parseFloat(theRow[cy]) + parseFloat(csvdata[eachRow][cy])
}
}
}
}
// Pushing final row
Logger.log(formattedMonth)
newOrders.push(theRow)
}
Logger.log(newOrders)
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName).getRange(2,12, newOrders.length, newOrders[0].length).setValues(newOrders)
}
}
Thanks in advance and let me know if anything needs to be clarified!
Not an answer as you haven't provided enough information, but some hints on where things might be going wrong.
You really should use semi-colons to end expressions, whoever left you this code to maintain needs to be reminded of their obligations to others.
One issue in your code is that currMonth is an empty string:
var currMonth = "";
...
if (eachRow > 8)
{
var date = csvdata[eachRow][2].split("-")
var month = date[0]
// This expression must always return false since currMonth == ""
if((currMonth != "") && (month.localeCompare(currMonth) != 0)) {
theRow[2] = formattedMonth
newOrders.push(theRow)
theRow = []
// this statement expression will never be executed,
// so currMonth remains an empty string
currMonth = month
for (var ci = 0; ci < columns; ci++){
// Reset data structure
theRow.push("0")
}
}
Also looking at:
for (var eachRow in csvdata){
if (eachRow>8)
presumably eachRow is a string, comparing it to a number using > will always return false if Number(eachRow) does not return a number (e.g. it will evaluate correctly if eachRow is a string like "12" or "6" but not a string like "2017-08-28").
You need to supply a sample for csvdata and the output you expect from processing it.
I'm having a really hard time adding this small function to my work here:
Basically if data[i][49] is blank I want to assign my name "Clay" to it and otherwise data[i][49]. I've tried putting it in multiple places and it just inserts the whole function body into my spreadsheet.
Thanks for any help!
var whywontthiswork = (function {if(data[i][49] == " "){
"Clay"
}else{
data[i][49]
};})
It keeps saying "Invalid property ID. (line 27, file "C1: Push Load Details")"
function pushLoadDetails() {
deleteCSV();
//allPlainTextFormat(); adds 20 seconds
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh0 = ss.getSheetByName("Loads"), sh1 = ss.getSheetByName("DAT"), sh2 = ss.getSheetByName("Truckstop"),
sh3 = ss.getSheetByName("Posteverywhere"),sh4 = ss.getSheetByName("Push"), sh5 = ss.getSheetByName("Carriers");
// get data from Loads sheet
var data = sh0.getRange(8,4,33,55).getValues();
// create array to hold data (imaginary)
var aMain = new Array();
var bMain = new Array();
var myArray = aMain;
// itterate through data and add to array
//for (var i = 0, len = myArray.length; i < len; i++) {}
for(var i=0; i < 31; i++) { //Controls how many rows it goes through for posting // len = data.length; i < len
var whywontthiswork = (function {if(data[i][49] == " "){
"Clay"
}else{
data[i][49]
};})
if(i != ""){
for(var j=25; j<28; j++) { //Controls which date elements to iterate through for posting
for(var k=32; k<37; k++) { //Controls which trailer type elements to iterate through for posting
var aPushArray = [data[i][0],data[i][1],data[i][2],data[i][3],data[i][4],data[i][5],data[i][6],data[i][7],data[i][8],data[i][9],
data[i][10],data[i][11],data[i][12],data[i][13],data[i][14],data[i][15],data[i][16],data[i][17],data[i][18],data[i][19],
data[i][20],data[i][21],data[i][22],data[i][23],data[i][24],data[i][j],//data[i][25],//data[i][26],data[i][27],
data[i][28],data[i][29],
data[i][30],data[i][31],data[i][k],//data[i][32],data[i][33],data[i][34],data[i][35],data[i][36],
data[i][37],data[i][38],data[i][39],
data[i][40],data[i][41],data[i][42],data[i][43],data[i][44],data[i][45],
data[i][46],data[i][47],data[i][48],[whywontthiswork]//data[i][49]
]
//[Utilities.formatDate(new Date(), "GMT+8", "ddMMyyyyHHmmssSSS")] can go over data[i][44] to make consecutive numbers
//var aJK = Logger.log(aPushArray[0].length);
//var aWidth = return aJK;
//ts = aPushArray.split(',');
var aPush = aMain.push(aPushArray); //add more data sets to import more columns of data
};
};
};
};
// function pushCarrierInfo(){
for(var i=0; i < 31; i++) {
if(i != ""){ //Controls how many rows it goes through for posting
//Controls which trailer type elements to iterate through for posting
deleteSheet("Carriers","A2:AZ")
var bPush = bMain.push(
[data[i][49],data[i][46],data[i][48],data[i][47],
data[i][0],data[i][1],data[i][2],data[i][3],data[i][4],data[i][5],data[i][6],data[i][7],data[i][8],//data[i][9],
//data[i][10],data[i][11],data[i][12],data[i][13],data[i][14],data[i][15],data[i][16],data[i][17],data[i][18],
data[i][19],
//data[i][20],data[i][21],data[i][22],data[i][23],data[i][24],data[i][25],data[i][26],data[i][27],
data[i][28],data[i][29],
data[i][30],data[i][31],//data[i][32],data[i][33],data[i][34],data[i][35],data[i][36],
data[i][37],data[i][38],data[i][39],
data[i][40],data[i][41],data[i][42],data[i][43],data[i][44],data[i][45]//,data[i][46],data[i][48],data[i][49],
//data[i][50],data[i][51],data[i][52],data[i][53]
]); //add more data sets to import more columns of data
};
};
// };
//var aWidth = Logger.log(aPushArray[0].length);
//var bWidth = Logger.log(bPush[0].length);
// add array of data to second sheet. final column length must match # of data sets
sh4.getRange(2, 1, aMain.length, 44).setValues(aMain);
sh5.getRange(2, 1, bMain.length, 27).setValues(bMain);
deleteSheet("Posteverywhere","A3:Z")
//allPlainTextFormat(); adds 20 seconds
//var ss = SpreadsheetApp.getActive();
var ssk = SpreadsheetApp.openById("15bdmuKR9aHZyjffpzN0wFqhnJf6klFmni64OnA3HEEo");
var shk = ssk.getSheetByName("Posteverywhere");
var thissheet = ss.getSheetByName("Posteverywhere"); //row, column, numRows, numColumns
//var thissheet = thisSheet.getRange(4,1,50,30);
// get data from Loads sheet
var dataK = shk.getRange(1,1,34,52).getValues();
//create array to hold data (imaginary)
var koturMain = new Array();
// itterate through data and add to array
for(var i=0; i < 31; i++) {
if(i != ""){ //Controls how many rows it goes through for posting
//Controls which trailer type elements to iterate through for posting
var koturPE = koturMain.push([dataK[i][0],dataK[i][1],dataK[i][2],dataK[i][3],dataK[i][4],dataK[i][5],dataK[i][6],dataK[i][7],dataK[i][8],dataK[i][9],
dataK[i][10],dataK[i][11],dataK[i][12],dataK[i][13],dataK[i][14],dataK[i][15],dataK[i][16],dataK[i][17],dataK[i][18],dataK[i][19],
dataK[i][20],dataK[i][21],dataK[i][22],dataK[i][23],dataK[i][24],dataK[i][25],dataK[i][26],dataK[i][27],dataK[i][28],dataK[i][29],
dataK[i][30],//data[i][31],//data[i][32],data[i][33],data[i][34],data[i][35],data[i][36],
//data[i][37],data[i][38],data[i][39],
//data[i][40],data[i][41],data[i][42],data[i][43],data[i][45],data[i][44],//data[i][46],data[i][47],data[i][48],data[i][49]
]); //add more data sets to import more columns of data
};
};
// add array of data to second sheet. final column length must match # of data sets
// thissheet.getRange(thissheet.getLastRow()+1, 1, koturMain.length, 25).setValues(koturMain);
};
After trying a number of things I realized this would get the job done without increasing processing time and having to mess with formatting or inserting any functions.
Using the or "||" symbol is and if/else statement.
[data[i][49]||"Clay"]