Declaring Global Array in Google Spreadsheet - javascript

I am trying speedup the processing/calculation in Google spreadsheet by putting my bonus setting into global array using PropertiesServices instead of loading the bonus % from the Setting sheet whenever user update their Daily sheet.
The method I used seem not working as I expected, the FOR loop is unable to look for "ALL". Hope someone able to give some advice.
I suspected that JSON structure is different from my 2D array, but I don't know how to solve it, I am new to javascripts.
Setting sheet only containing 4 columns: Game, StartDate, EndDate, Bonus %
p/s: This is a simplified scripts.
function onOpen(event) {
var bonusSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Setting");
var bonusCompany = bonusSheet.getRange("A46:D503").getValues();
PropertiesService.getDocumentProperties().setProperty("bonusCompany", JSON.stringify(bonusCompany));
}
function onEdit(event) {
var colGame = "H";
var colBonus = "K";
var numColBonus = 11;
var nPoint;
var sheet = event.source.getActiveSheet();
var row = sheet.getActiveCell().getRow();
var col = sheet.getActiveCell().getColumn();
var cGame = sheet.getRange(colGame+row).getValue();
var dDate = new Date();
for(var k=0, kLen=bonusCompany.length; k<kLen; k++)
{
if((bonusCompany[k][0] == cGame || bonusCompany[k][0] == "ALL") && dDate.valueOf() >= bonusCompany[k][1] && dDate.valueOf() <= bonusCompany[k][2] ){
sheet.getRange(event.range.rowStart,numColBonus,event.range.rowEnd-event.range.rowStart+1,1).setValue(nPoint*bonusCompany[i][4]/100);
return;}
}
sheet.getRange(event.range.rowStart,numColBonus,event.range.rowEnd-event.range.rowStart+1,1).setValue(0);
}

If you want to use PropertiesService.getDocumentProperties(), how about following sample script?
Your script is almost correct. When the data is set by PropertiesService.getDocumentProperties().setProperty(), the data can be retrieved by PropertiesService.getDocumentProperties().getProperty(). The detail information is https://developers.google.com/apps-script/reference/properties/properties-service.
And when the array data is set by setProperty(), the array data is stored as strings by JSON.stringify(). So it is necessary to convert from the string data to the array data. Fortunately, it has already known that the values retrieved by getValues() from spreadsheet is 2 dimensional array. So the string data can be converted to an array data using the regular expression. The sample script is as follows. I added 2 lines to your script. Please check them.
Script :
function onOpen(event) {
var bonusSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Setting");
var bonusCompany = bonusSheet.getRange("A46:D503").getValues();
PropertiesService.getDocumentProperties().setProperty("bonusCompany", JSON.stringify(bonusCompany));
}
function onEdit(event) {
var colGame = "H";
var colBonus = "K";
var numColBonus = 11;
var nPoint;
var sheet = event.source.getActiveSheet();
var row = sheet.getActiveCell().getRow();
var col = sheet.getActiveCell().getColumn();
var cGame = sheet.getRange(colGame+row).getValue();
var dDate = new Date();
var d = PropertiesService.getDocumentProperties().getProperty("bonusCompany").split(/^\[|\]$/)[1].split(/\[(.*?)\]/); // I added this line.
var bonusCompany = [[(e=="\"\"" ? "" : e) for each (e in f)] for each (f in [d[j].split(",") for (j in d) if(j%2==1)])]; // I added this line.
for(var k=0, kLen=bonusCompany.length; k<kLen; k++) {
if((bonusCompany[k][0] == cGame || bonusCompany[k][0] == "ALL") && dDate.valueOf() >= bonusCompany[k][1] && dDate.valueOf() <= bonusCompany[k][2] ){
sheet.getRange(event.range.rowStart,numColBonus,event.range.rowEnd-event.range.rowStart+1,1).setValue(nPoint*bonusCompany[i][4]/100);
return;
}
}
sheet.getRange(event.range.rowStart,numColBonus,event.range.rowEnd-event.range.rowStart+1,1).setValue(0);
}
I don't know data of your spreadsheet. So if this modification doesn't work, can I ask you about the data of your spreadsheet?

Related

ReDash to Google Sheets using Google Script - How do I add the date at the end?

I'm trying to use the following Google script to add data from Redash, then add the current date at the end. Where could I add a var now = new Date(); to input the date on the last row?
The desired outcome is the following. A-C is the data I'm successfully already pulling from Redash, but I want to add the current date. The date will always be in F if that helps.
https://www.dropbox.com/s/lfu9jk06j0fduo8/Screenshot%202020-05-20%2018.44.26.png?dl=0
Thank you for your help!
//FUNCTION FOR GETTING DATA FROM REDASH QUERIES
function getapidata(response, sheetname) {
var array1 = response.getContentText(); //Store data from redash into this variable
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetname);
var iLen = array1.length;
var i = 0;
var stringvalue1 = "";
var s1 = "";
var num1 = 1;
var num2 = 1;
var noheader = 1;
for (i = 0; i < iLen; i++) {
var j = i;
do {
s1 = array1[j];
if (s1 == ",") {
if (noheader == 1)
noheader = 1;
else
sheet.getRange(sheet.getLastRow() + num1, num2).setValue(stringvalue1);
num1 = 0;
num2++;
stringvalue1 = "";
} else
stringvalue1 = stringvalue1 + s1;
j++;
} while (s1 != "\n")
if (noheader == 1)
noheader = 1;
else
sheet.getRange(sheet.getLastRow() + num1, num2).setValue(stringvalue1);
noheader = 2;
num1 = 1;
num2 = 1;
stringvalue1 = "";
i = j - 1;
}
}
function getfromRedash() {
//Current SHR (Max Preps)
var redashapi = "API"; //Storing Redash API Value.
var sheetname = "SHEETTITLE"; //Assign your sheetname(where you would want data to be imported) to this variable
var response = UrlFetchApp.fetch(redashapi, {
muteHttpExceptions: true
}); //Storing Redash Cached Query result in this variable.
Logger.log(response.getContentText()); //If any error, error to be logged
getapidata(response, sheetname); //Call function for writing data in google sheets
//The 5 lines of code above can be repeated for multiple sheets in a single google spreadsheet file. For. Eg. If you have 5 different sheets in a single google worksheet, you can just copy paste the above lines of code, and just change the variable “redashapi” so as to make calls to the appropriate redash queries.
}
//THIS FUNCTION IS TO MAKE A NEW MENU IN GOOGLE SHEETS
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('MENU')
.addItem('Get Data from Redash', 'getfromRedash')
.addToUi();
}
Well, you row number addressing is a bit confusing, cause sheet.getLastRow() would change every time you add values after the last row, but you can write something like
sheet.getRange(sheet.getLastRow() + num1, 6).setValue(new Date());
to insert date. You can refer other answers, like
How to format a JavaScript date
for date formatting.

Script on Google Sheet to Run Function

Sample Data Sheet
Hi,
I currently have a runOnEdit script (below) that is set to trigger on changes in Column A, then pull the value from Column D and run it through a function and return the value in Column E. It works great. However, because I am using Zapier to push changes into the trigger column, the runOnEdit function is not working. Based on research, I know that onEdit wont detect changes from API services. So I would like to create a similar function that runs hourly on Google Sheets that basically checks a timestamp column (B) and compares it to another timestamp column (C). If it meets the if condition, then I want it to return the function value in the return column (E) for each cell. I'm using the timestamps to determine whether the trigger column was updated recently in which case I want the sub function to run and if it has not been run recently, then I want it to stop and leave the current value that exists in the result column E.
This is my current runOnEdit script that works great.
function runonEdit(e) {
var sh = e.source.getActiveSheet();
if(sh.getName() !== "Copy of DataFinal" || e.range.columnStart !== 1) return;// 1 is A, 2 is B, 3 is C and so on
var v = e.range.offset(0,5)
var value =v.getValue()
var o = e.range.offset(0, 4)
if (value == "") {
o.setValue("")
return null;
}
var timedis = gettime(value)
o.setValue(timedis)
}
function gettime(f) {
var res= UrlFetchApp.fetch("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins="+f+"&key=MYKEY");
var content = res.getContentText();
var json = JSON.parse(content);
Logger.log(content);
var time=json.rows[0].elements[0].duration.text;
var dis=json.rows[0].elements[0].distance.text;
var timedis=time+"|"+dis
return timedis
}
Here is the attempt at my new script to run hourly. But i can't seem to figure it out.
'''
function runtimedisfunc() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Copy of DataFinal'); //source sheet
var testrange = sheet.getRange('A:Z'); //range to check
var o = testrange.range.offset(0, 5)
var tsza = testrange.range.offset(0,2)
var tstdfunc = testrange.range.offset(0,3)
var v = e.range.offset(0,4)
var combaddvalue =v.getValue()
var timestampza= tsza.getValue()
var timestamptdfunc= tstdfunc.getValue()
if (timestampza < timestamptdfunc){
return null;
}
if (combaddvalue == "") {
o.setValue("")
return null;
}
if ((timestampza != "") || (timestampza > timestamptdfunc)){
var timedis = gettime(combaddvalue)
o.setValue(timedis)
tstdfunc.setValue(new Date())
}
}
function gettime(f) {
var res= UrlFetchApp.fetch("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins="+f+"&key=MYKEY");
var content = res.getContentText();
var json = JSON.parse(content);
Logger.log(content);
var time=json.rows[0].elements[0].duration.text;
var dis=json.rows[0].elements[0].distance.text;
var timedis=time+"|"+dis
return timedis
}
'''
Summary
You want to regularly populate column E based on data from column D
You want to do so only if the data in column D is not ""
Issue
As you noticed onEdit does not trigger for edits performed by an API
You want to run your script hourly rather than onEdit
Solution
Use an installable timed-driven trigger instead of the onEdit trigger
This will allow to run your script hourly without any implementation of timestamps or similarfeatures in you code
Just keep in mind that time-driven triggers don't support event objects like e.g. e.range - since there might not be any range edited at the moment when the trigger fires
You need to rewrite your function in such a way that it is not reliant on event objects
Sample:
function bindATimedrivenTriggerToMe() {
var sh = SpreadsheetApp.getActive().getSheetByName("Copy of DataFinal");
var lastRow = sh.getLastRow();
var v = sh.getRange(1,4,lastRow,1);
var values =v.getValues();
var o = sh.getRange(1,5,lastRow,1);
var valueArray = [];
for (var i =0; i < values.length; i++){
var value = values[i][0];
valueArray[i] = [];
if (value == "") {
valueArray[i].push("");
} else {
var timedis = gettime(value);
valueArray[i].push(timedis);
}
}
o.setValues(valueArray);
}
function gettime(f) {
var res= UrlFetchApp.fetch("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins="+f+"&key=MYKEY");
var content = res.getContentText();
var json = JSON.parse(content);
Logger.log(content);
var time=json.rows[0].elements[0].duration.text;
var dis=json.rows[0].elements[0].distance.text;
var timedis=time+"|"+dis
return timedis
}

Trying to paste Values from formula Google App Script

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])');
}
}

Javascript: Grouping csv data by month instead of day before inserting to Google Sheets

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.

Google script says - Exceeded maximum execution time

I am using the below script to delete duplicate rows from the google spreadsheet. The script was working good but as the data in the spreadsheet is being added daily, now the script is throwing "Exceeded maximum execution time" error. As I am new to scripting I don't understand what is my problem.
Could someone help me in solving this problem of mine.
function Deleteduplicates() {
var SpreadSheetKey = "My key";
var sheetD = SpreadsheetApp.openById(SpreadSheetKey).getSheetByName("Daily");
var sheetW = SpreadsheetApp.openById(SpreadSheetKey).getSheetByName("Weekly");
var dataD = sheetD.getDataRange().getValues();
var dataW = sheetW.getDataRange().getValues();
//Daily
var newDataD = new Array();
for(i in dataD){
var row = dataD[i];
var duplicate = false;
for(j in newDataD){
if(row.join() == newDataD[j].join()){
duplicate = true;
}
}
if(!duplicate){
newDataD.push(row);
}
}
//weekly
var newDataW = new Array();
for(i in dataW){
var row = dataW[i];
var duplicate = false;
for(j in newDataW){
if(row.join() == newDataW[j].join()){
duplicate = true;
}
}
if(!duplicate){
newDataW.push(row);
}
}
sheetD.clearContents();
sheetW.clearContents();
sheetD.getRange(1, 1, newDataD.length, newDataD[0].length).setValues(newDataD);
sheetW.getRange(1, 1, newDataW.length, newDataW[0].length).setValues(newDataW);
}
Conceptually, this should be quite a bit faster. I have not tried it on a large data set. The first version will leave the rows sorted as they were originally. The second version will be faster but will leave the rows sorted according to the columns from first to last on first text.
function Deleteduplicates() {
var SpreadSheetKey = "My key";
var ss = SpreadsheetApp.openById(SpreadSheetKey);
var sheetD = ss.getSheetByName("Daily");
var sheetW = ss.getSheetByName("Weekly");
var sheets = [sheetD, sheetW];
var toSs = {};
for(s in sheets) {
var data = sheets[s].getDataRange().getValues();
for(i in data){
// EDIT: remove commas from join("") for blank test
data[i].unshift(data[i].join(""),(1000000 + i).toString());
}
data.sort();
// remove blank rows -- Edit
var blank = 0;
while(data[blank][0].trim().length == 0) {blank++};
if(blank > 0) data.splice(0, blank);
// end Edit
var len = data.length - 1;
for(var x = len; x > 0; x-- ) {
if(data[x][0] == data[x-1][0]) {
data.splice(x, 1);
};
};
for(i in data) {
data[i].splice( 0, 1);
};
data.sort();
for(i in data) {
data[i].splice(0, 1);
};
toSs[sheets[s].getSheetName()] = data;
};
for(s in sheets) {
var data = toSs[sheets[s].getSheetName()];
sheets[s].clearContents();
sheets[s].getRange(1, 1, data.length, data[0].length).setValues(data);
}
}
Faster leaving rows sorted by join() created to test for duplicates
function Deleteduplicates() {
var SpreadSheetKey = "My key";
var ss = SpreadsheetApp.openById(SpreadSheetKey);
var sheetD = ss.getSheetByName("Daily");
var sheetW = ss.getSheetByName("Weekly");
var sheets = [sheetD, sheetW];
var toSs = {};
for(s in sheets) {
var data = sheets[s].getDataRange().getValues();
for(i in data){
// EDIT: remove commas from join("") for blank test
data[i].unshift(data[i].join(""));
}
data.sort();
// remove blank rows -- Edit
var blank = 0;
while(data[blank][0].trim().length == 0) {blank++};
if(blank > 0) data.splice(0, blank);
// end Edit
var len = data.length - 1;
for(var x = len; x > 0; x-- ) {
if(data[x][0] == data[x-1][0]) {
data.splice(x, 1);
};
};
for(i in data) {
data[i].splice( 0, 1);
};
toSs[sheets[s].getSheetName()] = data;
};
for(s in sheets) {
var data = toSs[sheets[s].getSheetName()];
sheets[s].clearContents();
sheets[s].getRange(1, 1, data.length, data[0].length).setValues(data);
}
}
Edited per Henrique's comment.
Edited 5/8: Remove blank rows(2 edited areas marked)
There is no problem with your script. It is just exceeding the "maximum execution time" allowed for any script (which is currently 6 minutes).
To workaround this problem you'll have to split your problem into "less than 6 minutes" parts.
For example, in your code you're clearing duplicates from 2 sheets. Trying creating two functions, one for each, and run them separately.
Also, there could be some performance enhancements that could make the script run under 6 minutes. For example, I'm not sure joining each row is the best way (performance-wise) to do an array comparison.
Creating a new array to re-set the data might not be optimal either, I'd probably go with a map verification, which is constant-time, instead of O(n^2) double array checking you're doing.
Bottom line, this is a limitation you have to live with in Apps Script. And any solution anyone proposes is just a workaround, that will also eventually fail if your data gets overly big.

Categories

Resources