Service invoked too many times in a short time - javascript

I'm getting this error message to the following script. "Service invoked too many times in a short time: exec qps. Try Utilities.sleep(1000) between calls."
I've given my code below. Can you help me to stop this error message? Note: I'm using an array formula imported from another sheet. The trigger is set to work on Change.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Direct');
var range = sheet.getDataRange();
var values = range.getValues();
var rows_deleted = 0;
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
var value = values[i][j];
//row numbers are 1-based, not zero-based like this for-loop, so we add one AND...
//every time we delete a row, all of the rows move down one, so we will subtract this count
var row = i + 1 - rows_deleted;
//if the type is a number, we don't need to look
if (typeof value === 'string') {
var result = value.search("Remove");
//the .search() method returns the index of the substring, or -1 if it is not found
//we only care if it is found, so test for not -1
if (result !== -1) {
sheet.deleteRow(row);
rows_deleted++;
}
}
}
}
};

Related

How to get row number of an array that's being looped through?

Using Google AppScripts and found this script online which I've modified.
NOTE: Spreadsheet has headers.
Run through the values found in 3rd column
If any of the values include the word "USD", copy the entire row into "Target Sheet"
Delete that row after it's finished copying
Continuing looping through until it finds the next "USD"...etc.
What Works:
I'm successfully able to loop through the array and copy the correct rows into the "Target Sheet"
What I Need Help With:
I can't figure out how to delete the row from the original sheet. It always ends up deleting the row before, then skips 1 row every time it loops again. I've tried multiple different formats to this portion of the code, such as i-1, i+1, etc... Not sure what I'm doing wrong here:
if (i == 0) {
ss1.deleteRow(i+2);
} else {
ss1.deleteRow(i)
}
I've pasted the entire script below:
var etfar = ["Cash File"] //This is a string because I have multiple sheets I'm looping through
function cashCopy(etf) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ss1 = ss.getSheetByName(etf);
var ss2 = ss.getSheetByName("Target Sheet");
var lr = ss1.getLastRow();
var lc = ss1.getLastColumn();
// gets the data in Cash File
var range = ss1.getRange(1, 1, lr, lc);
var data = range.getValues();
// loops through rows in data
for (var i = 0; i < data.length; i++) {
var check = data[i][2] // ith row, 3rd column
if (check.includes("USD")) {
var rowToCopy = data[i];
ss2.appendRow(rowToCopy);
if (i == 0) {
ss1.deleteRow(i+2);
} else {
ss1.deleteRow(i)
}
};
}; // end i
}
for (var i = 0; i < etfar.length; i++) {
cashCopy(etfar[i])
}
Explanation:
One way to iteratively delete rows in a sheet is to create a backwards for loop.
Replace:
for (var i = 0; i < data.length; i++)
with:
for (var i = data.length - 1; i >= 0; i--)
In this way, every time you delete a row, the data will still correspond to the correct row.
Another issue is that you get var range = ss1.getRange(1, 1, lr, lc). That means, you start iterating from the first row (including the headers) and then you are using a workaround like that:
if (i == 0) {
ss1.deleteRow(i+2);
} else {
ss1.deleteRow(i)
}
But actually, you don't need to include the headers in the first place. Use this instead: var range = ss1.getRange(2, 1, lr, lc) and replace the if/else statement with just: ss1.deleteRow(i+2).
since data index starts from 0, but your range starts from row 2.
Solution:
var etfar = ["Cash File"] //This is a string because I have multiple sheets I'm looping through
function cashCopy(etf) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ss1 = ss.getSheetByName(etf);
var ss2 = ss.getSheetByName("Target Sheet");
var lr = ss1.getLastRow();
var lc = ss1.getLastColumn();
// gets the data in Cash File
var range = ss1.getRange(2, 1, lr, lc); // <- modification
var data = range.getValues();
// loops through rows in data
for (var i = data.length - 1; i >= 0; i--)
{
var check = data[i][2] // ith row, 3rd column
if (check.includes("USD")) {
var rowToCopy = data[i];
ss2.appendRow(rowToCopy);
ss1.deleteRow(i+2); // <- new code
};
}; // end i
}
for (var i = 0; i < etfar.length; i++) {
cashCopy(etfar[i])
}

Why does this for loop not work in Google scripts

i wrote the following loop based on the code found here:
How do I iterate through table rows and cells in javascript?
function myRowLooper() {
var inputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('INPUT');
var inputRange = inputSheet.getRange(2,inputSheet.getLastColumn(),inputSheet.getLastRow());
for (var i = 0, row; row = inputRange.rows[i]; i++) {
Logger.log(row);
for (var j = 0, col; col = row.cells[j]; j++) {
Logger.log(col);
}
}
}
but when I apply it to Google scripts it throws an error: "TypeError: Cannot read property "0" from undefined."
What's causing this?
Because you can't get any value from 'inputRange.rows[i]'.
You may do something like this :
function myRowLooper() {
var inputSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
for(var i = 1; i<=inputSheet.getLastRow(); i++){
for(var j = 1; j<=inputSheet.getLastColumn(); j++){
var cell = inputSheet.getRange(i,j).getValue();
Logger.log(cell);
}
}
}
Hope this will help you.
Thanks.
Your code is extremely sloppy. You are trying to combine variables and condense unnecessarily and it's leading to errors in your code. There's no use in added "efficiency" if it leads to errors and mistakes.
Try something like this --
function yourFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Name");
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getMaxColumns();
var conditionToCheckColumn = (lastColumn - 1);
var conditionRange = sheet.getRange(2, checkColumn, (lastRow - 1), 1);
var check = checkRange.getValues();
for (var i = 0; i < check.length; i++) {
if (check[i] == condition) {
continue;
} else {
//your code here
}
}
}
This will pull a range at which you can check it's value/ condition and if matches, it does nothing. If it does not match, it will perform code. It will then loop to the next row until the last row in your check range that has data.
Be warned - functions count as data despite the cell being visibly empty. If your sheet uses functions like =QUERY, you will have an infinitely looping code unless your =QUERY (or other fx()) has a specific upper limit.

Match two rows in google script

I'm new to google script and I want to compare two rows from two sheets & if any cell data value matches it'll increase a value. But I'm getting an error like this,
TypeError: Cannot read property "0" from undefined.
Here are my codes,
function onFormSubmit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form Responses 1");
var ans_sheet = ss.getSheetByName('Correct_Answers');
var getValues = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
var realValues = ans_sheet.getRange(2, 1, ans_sheet.getLastRow(), ans_sheet.getLastColumn()).getValues();
for (var i in getValues) {
for (var j in getValues[i]) {
var matched = 0;
if (getValues[i][j] == realValues[i][j]) {
matched++;
} else {
getValues.push(1);
}
}
}
I know I'm doing something wrong here but since I'm new I can't figure it out. Need this help badly from experts. Thanks.
This could be because the two ranges have different lengths or because you are appending 1's to the first array in which case there is also nothing to subscript.
At some point you are trying to access the first column (index 0) of a row that is not in the other range or the first column of 1 which does not exist.
since you are iterating through getValues you can do a check at the beginning of the loop.
for (var i in getValues) {
if(i == realValues.length){break;}
for (var j in getValues[i]) {
if(j == realValues[j].length){break;}
var matched = 0;
if (getValues[i][j] == realValues[i][j]) {
matched++;
}
}
}

Unable to filter data from other spreadhseet paste in master sheet based on dates in google spreadsheet

First of all thank you for the efforts made in this site. As an individual and a beginner i have learnt from my errors made. Thanks for all who have contributed & extended their support.
Thanks for this.
Here is a small program written which is not working (no output)seen , i have tried it in many ways but in vain. please help me to find a solution for this.
the aim of this program was to filter the data from 4 sheets and paste into current sheet (master). this filter is based on date values.
Conditions of dates are taken from the master sheet in columns in (b2 & d2)dates. this are to be filtered out based in column no.18 which has dates in client sheets.
function myFunction3() {
var source = ['0AjkkHlm3kCphdGhSWnlxWmFsakZ2aFhMSHl6SlF3M1E',
'0AjkkHlm3kCphdHY2aXpjTVJEMlFRYVBST0ZPYzNwRFE',
'0AjkkHlm3kCphdEc5ZHFpeHVlc241SlFKWGJDeXFKLXc',
'0AjkkHlm3kCphdG9WVjVRRnQ3RlFlcllhd1JGallXVmc'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get start date from sheet
var sDate = ss.getSheetByName('123').getRange("B2").getValue();
// get end date from sheet
var eDate = ss.getSheetByName('123').getRange("D2").getValue();
// days between
var Dura = ss.getSheetByName('123').getRange("E1").getValue();
var codes = new Array();
for (var k = 0; k < Dura; k++){
var d = new Date(sDate);
d.setDate(d.getDate()+ k);
codes[k] = d;
}
var numCodes = codes.length;
var copied = [];
for (var k = 0; k < numCodes; k++) {
copied[k] = [];
}
//get data from external sheets for comparision
for (var i = 0; i < source.length; i++) {
var tempCopy = SpreadsheetApp.openById(source[i]).getSheetByName('Footfall-Format').getDataRange().getValues();
// comparision starts
for (var j = 0; j < tempCopy.length; j++) {
var codeIndex = codes.indexOf(tempCopy[j][5]);
if (codeIndex > -1) copied[codeIndex].push(tempCopy[j]);
}
}
var sheets = SpreadsheetApp.getActive().getSheets();
for (var m = 0; m < numCodes; m++) {
if (copied[m][0] != undefined) {
var gensheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('123');
gensheet.getRange(5, 1, 1500, 18).clear({contentsOnly:true});
gensheet.getRange(5, 1, copied[m].length, copied[m][0].length).setValues(copied[m]);
}
}
}
The fundamental problem is that you are comparing objects for equality - in this case, you're comparing Date objects. Even when the date represented by two of these objects is the same, the object comparison comes up false unless you're actually referencing the same object. You can read more about this in Compare two dates with JavaScript.
Here's a simple change to your script, using toDateString(), that will ensure your codes[] array contains string values that can be compared with values in tempCopy[j][5].
function myFunction3() {
var source = ['0AjkkHlm3kCphdGhSWnlxWmFsakZ2aFhMSHl6SlF3M1E',
'0AjkkHlm3kCphdHY2aXpjTVJEMlFRYVBST0ZPYzNwRFE',
'0AjkkHlm3kCphdEc5ZHFpeHVlc241SlFKWGJDeXFKLXc',
'0AjkkHlm3kCphdG9WVjVRRnQ3RlFlcllhd1JGallXVmc'];
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get start date from sheet
var sDate = ss.getSheetByName('123').getRange("B2").getValue();
// get end date from sheet
var eDate = ss.getSheetByName('123').getRange("D2").getValue();
// days between
var Dura = ss.getSheetByName('123').getRange("E1").getValue();
var codes = new Array();
for (var k = 0; k < Dura; k++){
var d = new Date(sDate);
d.setDate(d.getDate()+ k);
codes[k] = d.toDateString(); //***** Make array of Strings, not Dates
}
var numCodes = codes.length;
var copied = [];
for (var k = 0; k < numCodes; k++) {
copied[k] = [];
}
//get data from external sheets for comparision
for (var i = 0; i < source.length; i++) {
var tempCopy = SpreadsheetApp.openById(source[i]).getSheetByName('Footfall-Format').getDataRange().getValues();
// comparision starts
for (var j = 4; j < tempCopy.length; j++) { // start at 4 to skip headers
if (typeof tempCopy[j][5] != "object") break; // skips strings, but could improve
// Search for String match of date from input record
var codeIndex = codes.indexOf(tempCopy[j][5].toDateString());
if (codeIndex > -1) copied[codeIndex].push(tempCopy[j]);
}
}
// This part has bugs... each day overwrites the previous
var sheets = SpreadsheetApp.getActive().getSheets();
for (var m = 0; m < numCodes; m++) {
if (copied[m][0] != undefined) {
var gensheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('123');
gensheet.getRange(5, 1, 1500, 18).clear({contentsOnly:true});
gensheet.getRange(5, 1, copied[m].length, copied[m][0].length).setValues(copied[m]);
}
}
}
As #Serge points out, there are other problems in this code.
d.getDate()+ k does not handle month-end, so you need to do that yourself.
The last part of your script that handles the output via setValues() needs to be debugged. As it is, each day overwrites the previous day's values from the copied[] array. I'm not sure what requirement you were trying to meet with this, so I left it alone, but it needs attention.

How can I remove rows with unique values, keeping rows with duplicate values?

I have a spreadsheet of surveys, in which I need to see how particular users have varied over time. As such, I need to disregard all rows with unique values in a particular column. The data looks like this:
Response Date Response_ID Account_ID Q.1
10/20/2011 12:03:43 PM 23655956 1168161 8
10/20/2011 03:52:57 PM 23660161 1168152 0
10/21/2011 10:55:54 AM 23672903 1166121 7
10/23/2011 04:28:16 PM 23694471 1144756 9
10/25/2011 06:30:52 AM 23732674 1167449 7
10/25/2011 07:52:28 AM 23734597 1087618 5
I've found a way to do so in Excel VBA:
Sub Del_Unique()
Application.ScreenUpdating = False
Columns("B:B").Insert Shift:=xlToRight
Columns("A:A").Copy Destination:=Columns("B:B")
i = Application.CountIf(Range("A:A"), "<>") + 50
If i > 65536 Then i = 65536
Do
If Application.CountIf(Range("B:B"), Range("A" & i)) = 1 Then
Rows(i).Delete
End If
i = i - 1
Loop Until i = 0
Columns("B:B").Delete
Application.ScreenUpdating = True
End Sub
I'd like to do it in Google Spreadsheets with a script that won't have to be changed. Closest I can get is retrieving all duplicate user ids from the range, but can't associate that with the row. That code follows:
function findDuplicatesInSelection() {
var activeRange = SpreadsheetApp.getActiveRange();
var values = activeRange.getValues();
// values that appear at least once
var once = {};
// values that appear at least twice
var twice = {};
// values that appear at least twice, stored in a pretty fashion!
var final = [];
for (var i = 0; i < values.length; i++) {
var inner = values[i];
for (var j = 0; j < inner.length; j++) {
var cell = inner[j];
if (cell == "") continue;
if (once.hasOwnProperty(cell)) {
if (!twice.hasOwnProperty(cell)) {
final.push(cell);
}
twice[cell] = 1;
} else {
once[cell] = 1;
}
}
}
if (final.length == 0) {
Browser.msgBox("No duplicates found");
} else {
Browser.msgBox("Duplicates are: " + final);
}
}
This is maybe not very efficient, but I think it's what you want:
var ar=[1,3,3,5,6,8,6,6];
console.log("Before:");
display(ar);//1 3 3 5 6 8 6 6
var index=[];
var ar2=[];
for(var a=0;a<ar.length;a++)
{
var duplicate=false;
for(var b=0;b<ar.length;b++)
{
if(ar[a]==ar[b]&&a!=b)
{
duplicate=true;
}
}
if(!duplicate)
{
index.push(a);
}
}
for(var a=0;a<index.length;a++)
{
ar[index[a]]=null;
}
for(var a=0;a<ar.length;a++)
{
if(ar[a]!=null)ar2.push(ar[a]);
}
console.log("After:");
display(ar2);//3 3 6 6 6
function display(x)
{
for(var a=0;a<x.length;a++)console.log(x[a]);
}
The fiddle : http://jsfiddle.net/mageek/6AGQ4/
And a shorter version that is as a function :
var ar=[1,3,3,5,6,8,6,6];
function removeUnique(x)
{
var index=[];
var ar2=[];
for(var a=0;a<ar.length;a++)
{
var duplicate=0;
for(var b=0;b<ar.length;b++)if(ar[a]==ar[b]&&a!=b)duplicate=1;
if(!duplicate)index.push(a);
}
for(var a=0;a<index.length;a++)ar[index[a]]=null;
for(var a=0;a<ar.length;a++)if(ar[a]!=null)ar2.push(ar[a]);
return x;
}
ar=removeUnique(ar);
The fiddle : http://jsfiddle.net/mageek/6AGQ4/2
I'd suggest going for something simple.
Create a short script that flags duplicates
Write the formula directly into the cell "=flagDuplicate(C2,C$2:C$10)"
Copy the forumla down the column
Use Spreadsheet's built in QUERY formula to pull the information you need
"=QUERY(A1:E10; "SELECT * WHERE E = TRUE"; 1)"
Here is a simple function to flag duplicates
function flagDuplicate(value, array) {
var duplicateCounter = 0;
for (var i=0; i<array.length; i++){
if (array[i] == value){ // I avoid === in Spreadsheet functions
duplicateCounter++;
}
}
if (duplicateCounter > 1){
return true;
}else{
return false;
}
}
Too many functions on a large table can slow things down. If it becomes a problem, you can always copy and "paste values only" - that will retain the information but remove the functions.
Best of luck.
Note: When I tested this I noticed that can take a while before the spreadsheet recognizes the new custom function (gives error like can't find function FLAGDUPLICATE)
You could also do it using arrays to handle the whole sheet at once :
function removeUnique(){
var col = 2 ; // choose the column you want to check for unique elements
var sh = SpreadsheetApp.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data=ss.getDataRange().getValues();// get all data
data.sort(function(x,y){
// var xp = Number(x[col]);// use these to sort on numeric values
// var yp = Number(y[col]);
var xp = x[col];// use these for non-numeric values
var yp = y[col];
Logger.log(xp+' '+yp); // just to check the sort is OK
return xp == yp ? 0 : xp < yp ? -1 : 1;// sort on column col numeric ascending
});
var cc=0;
var newdata = new Array();
for(nn=0;nn<data.length-1;++nn){
if(data[nn+1][col]==data[nn][col]||cc>0){
newdata.push(data[nn]);
++cc;
if(cc>1){cc=0}}
}
ss.getDataRange().clearContent(); // clear the sheet
sh.getRange(1,1,newdata.length,newdata[0].length).setValues(newdata);// paste new values sorted and without unique elements
}
EDIT : here is the version that keeps all duplicates (the working one)
function removeUnique(){
var col = 2 ; // choose the column you want to check for unique elements
var sh = SpreadsheetApp.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data=ss.getDataRange().getValues();// get all data
data.sort(function(x,y){
// var xp = Number(x[col]);// use these to sort on numeric values
// var yp = Number(y[col]);
var xp = x[col];// use these for non-numeric values
var yp = y[col];
Logger.log(xp+' '+yp); // just to check the sort is OK
return xp == yp ? 0 : xp < yp ? -1 : 1;// sort on column col numeric ascending
});
var newdata = new Array();
for(nn=0;nn<data.length-1;++nn){
if(data[nn+1][col]==data[nn][col]){
newdata.push(data[nn]);
}
}
if(data[nn-1][col]==data[nn][col]){newdata.push(data[nn])}
ss.getDataRange().clearContent(); // clear the sheet
sh.getRange(1,1,newdata.length,newdata[0].length).setValues(newdata);// paste new values sorted and without unique elements
}

Categories

Resources