Google sheets script - mass find and replace - google-apps-script

I have adapted the script posted here slightly to suit my needs. It has worked very well for the most part searching through a list of over 4500 records and replacing based on a list of cross references 187 long.
function replMyText(){
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('summary');
var sh2=ss.getSheetByName('Dashboard');
var rgtxt=sh1.getRange('A7:A4934');//text to replace
var rgrep=sh2.getRange('K2:L188');//replacement table
var txtA=rgtxt.getValues();
var repA=rgrep.getValues();
for(var i=0;i<txtA.length;i++){
for(var j=0;j<repA.length;j++){
if(txtA[i][0]==repA[j][0]){
txtA[i][0]=repA[j][1];
}
}
}
rgtxt.setValues(txtA);
}
There are however 17 items that did not get replaced. The error I see on the script page is "Item already exists". I can't see any difference in those 17 values compared to the ones already replaced. Could anyone provide some guidance as to what the error is referring to ?
Edit: In response to the 1st comment, yes, it would have been handy to have a line number but there was no such thing given. Here is the screen grab of the message-
...and here is the xref of old item / new item. The list has been shortened as all of the successful replacements have been removed.
The execution transcript shows as follows -
Apologies for not supplying more data up front.

It turns out the issue is not with the script. The error being returned is actually a "referred" error from the worksheet where the values are being replaced. There was data validation set on that particular column to check for and try to prevent duplicate values from being entered. That was the error message that was being returned. When I suspended the data validation the script completed without error.

Related

"Service Spreadsheets failed" error when calling insertCells method

This may be a simple issue, but I am having some trouble with a section of code.
Here is the code:
function CreateSheet() {
function toTitleCase(str) {
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0)
.toUpperCase() + txt.substr(1)
.toLowerCase();
});
}
var ss = SpreadsheetApp.getActive();
var templatesheet = ss.getSheetByName('Template');
//var fieldName = Browser.inputBox('Field Name', 'Insert Field Name', Browser.Buttons.OK_CANCEL);
var ui = SpreadsheetApp.getUi();
var fieldResult =
ui.prompt(
'FIELD NAME',
'Please type in the Field Name',
ui.ButtonSet.OK
)
var fieldName = toTitleCase(fieldResult.getResponseText());
var acreResult = ui.prompt('ACRES',
'Please type in the # of acres, if the # is not known just leave it blank',
ui.ButtonSet.OK
)
var acres = acreResult.getResponseText();
var url = '';
ss.insertSheet(fieldName,3,{template: templatesheet});
ss.getRange('B3:D3').activate();
ss.getCurrentCell().setValue(fieldName);
ss.getRange('E3').activate();
ss.getCurrentCell().setValue(acres);
url += '#gid=';
url += ss.getSheetId();
ss.setActiveSheet(ss.getSheetByName('Summary'));
SpreadsheetApp.setActiveSpreadsheet(ss);
ss.getRange('A5:J5').activate();
ss.getRange('A5:J5').insertCells(SpreadsheetApp.Dimension.ROWS);
The last line is where it is throwing the exception message:
Service Spreadsheets failed while accessing document with id..
If I go into the sheet itself and manually insert range 'A5:J5' then run the code it will work. What am I missing here?
Service Spreadsheets failed while accessing document with id
is usually an error message one obtains when hitting the maximum size limit for a spreadsheet
This limit is 5 000 000 cells, so it should not be the issue for your spreadsheet
However, several users experienced the same problem as you and have reported it on Google's Public Issue Tracker here or here
It is likely to be a bug and it currently being investigated by Google
Some users reported that they experience the problem only with V8 runtime
Try to disable it (Run > Disable new Apps Script runtime powered by V8), hopefully it will solve the issue temporarily
Otherwise, try to find a workaround replacing the line leading to the error
When I reproduce your script it does not error for me, this is why I can only give you some suggestions
If insertCells gives you trouble, try insertRowAfter() or similar instead
If the problem comes from ss.getRange('A5:J5'), try ss.getActiveSheet().getRange('A5:J5') or ss.getSheetByName('Summary').getRange('A5:J5')
Also, try removing the line SpreadsheetApp.setActiveSpreadsheet(ss); - you already defined ss as the active spreadsheet above, there is no need to set it to active again
I had this same problem related to insertCells. It drove me nuts for a couple of days but I finally just solved it! In case this helps someone else, here is a link to my answer as well as a copy/paste of it to be thorough.
This was a REALLY confusing issue to solve, as the errors reported by
the debugger did not point to the true culprit.
In another cell elsewhere on the sheet, I had this formula:
=SUMPRODUCT(LEN($A$5:$A)>0)
But since my inserted cells started with A5, this was causing the
error to be thrown when I would attempt the insert, if it tried to
insert ahead of A5. By removing this formula, the insert code works
perfectly every time, immediately.
So if you get this error and it is not related to having a sheet that
is too large etc., check to see if you have another formula somewhere
that is referencing the cell being inserted on.
Sometimes this error is caused by a process or calculation that was triggered by an earlier part of the script that has not completed by the time you try to insert those cells.
For example, if you have a formula that is summing a column in your summary sheet, that formula may still be calculating at the time you are trying to shift the rows that the sum formula is referencing. You can force the order of operations to complete those calculations before shifting the rows by adding SpreadsheetApp.flush(); before you insert new cells.
SpreadsheetApp.setActiveSpreadsheet(ss);
SpreadsheetApp.flush();
ss.getRange('A5:J5').activate();
ss.getRange('A5:J5').insertCells(SpreadsheetApp.Dimension.ROWS);
I had same issue.
First, you always need to make sure the sheet you are working on is an actual 'Google SpreadSheet' and not an uploaded one (for example, .xlsx or .xls).
Then use the Save as function to save the document as a Spreadsheed; this solution worked for me.
I found that a chart using the range caused this exception, remove the chart and it worked.
I ran into the same error message, with a sheet of 400 rows and 9 columns only. Inspired by the variouos proposals for solutions, I tried the following, and this did the trick:
duplicated the "orig sheet" to "copy of sheet"
deleted rows 4 to [last row] in "orig sheet" (so only 3 rows left)
copied rows 4 to [last row] from "copy of sheeet" back to "orig sheet"
and the script worked fine again :-)
(advantage: I did not have to change any formulas or scripts)

The coordinates of the range are outside the dimensions of the sheet (google-script)

I have a really very odd problem, which seems to have to do with the sequence i execute scripts. After investigating for hours, I cant explain it at all.
I have a google sheets script which gets emails from an email account and parses them according to given rules into a speadsheets.
I have many of those methods, all leveraging common classes like getEmails, etc.
Every single method of parsing works well and delivers the respected result. But when I run them in a big method one after the other it reports the error
"The coordinates of the range are outside the dimensions of the sheet."
after executing some of the methods correctly. The error occurs in the following line:
var resultArray = sheet.getRange(startrow, column, sheet.getLastRow(), 1).getValues();
and is based on the call
sheet.getLastRow()
(I can not even call this in the logger, it works for lets say 5 out of the 10 methods and then all the sudden i get the error)
Every of those methods parse a different email with a different pattern but does this only for new emails. Therefore I have to get the hashs of the old emails (thats the call) from the google sheets column 1 to work only on new email hashes. This process breaks somehow.
What is striking me is that i can execute any of the methods isolated without an error.
Any ideas?
As mentioned I have tried isolated and i have tried to change order or to run only 2 of the methods.. with the same result. I assume some variable is not set back properly... but i have no idea how that can lead to this error.
By the way: the code was working for the past few weeks without error (also for the combined method). The errors have started like a week ago without any code changes.
I came across the same issue.
This is a sample of the code that was causing the bug for me:
var priceSheet = ss.getSheetByName(priceSheetName);
var rangeToSort = priceSheet.getRange(2,1,priceSheet.getLastRow(),priceSheet.getLastColumn());
rangeToSort.sort(1);
ss.getSheetByName("my sheet").getRange(startingRow,pasteColumn,pasteHoldings.length,1).setValues(pasteHoldings);
The error The coordinates of the range are outside the dimensions of the sheet was raised on line 4 (similar to your scenario), but the issue was occurring when I was trying to sort a range that extended beyond the last row of the sheet i.e.
last row with contents = last row of the sheet
Sort row start range = 2
Number of rows = priceSheet.getLastRow() <-- this is impossible because the sort row start range is great than 1
The fix for me was to adjust the sort range down by the start row - 1
var priceSheet = ss.getSheetByName(priceSheetName);
var rangeToSort = priceSheet.getRange(2,1,priceSheet.getLastRow()-1,priceSheet.getLastColumn());
rangeToSort.sort(1);
ss.getSheetByName("my sheet").getRange(startingRow,pasteColumn,pasteHoldings.length,1).setValues(pasteHoldings);
This appears to be a bug in Google Sheets script: either the sort functionality, or at the very least the error handling is raising the error with reference to the wrong row.
My recommendation would be to check your use of .getLastRow() and see if it corresponds to a starting row greater than 1. Then adjust the .getLastRow() by starting row - 1
Would you happen to have your sort range starting on something other than row 1?
I was testing this out and I have my data to sort on A7:N100.
What I found was that the max row that I can have is the last row in the range minus the header rows that are not in the range. For example, I have the first 6 rows that aren't in the range. I have my last row in row 100. So my range is only working with A7:N94.
To solve, I ended up adding 6 blank rows to the bottom of my page and set sort range to what I wanted (A7:N100) and this worked.

Append only unique rows

I am trying to read an external JSON API and write parsed values from it into google sheet. So each API call writes a new row into the sheet. The second requirement is to write the row only if it contains something else than already inserted rows - in other words append new row only if it is unique.
I've finished the first requirement. I've used JSON.parse and appendRow and it works with no problem.
Unfortunately, I cannot get thru the second requirement. I can not figure any construction nor find an example solution.
Does anybody have an advice how to append only unique rows from google apps script?
EDIT: My apologize for the above inexact post. Here are the details.
Below mentioned code is my solution for the first requirement:
function run() {
var data = UrlFetchApp.fetch("https://url/json-api").getContentText();
var json = JSON.parse(data);
var last = (json.last);
var credit = parseInt(json.credit);
var doc = SpreadsheetApp.openById("googleSheetID");
var list = doc.getSheets()[0];
list.appendRow([last, credit]);
}
So it simply append new row each time I run the script. Unfortunately, the returned JSON changes only from time to time. When I scheduled the script to run every 5 minutes it leads to many redundant rows.
However I don't want to run any kind of distinct after the redundant rows are written. I'd like to check if the new parsed data is unique and if so - write, otherwise nothing.
getLastRow's value and check whether it's equal to last/credit. Then appendRow, if needed.
Script Sample Snippet:
var lastRow=list.getRange(1,list.getLastRow(),1,2).getValues(); //[[prev.last,prev.credit]]
if(lastRow[0][0]!=last && lastRow[0][1]!=credit){
list.appendRow([last, credit]);
}

google script reject spreadsheet submit

I have a function in a spreadsheet based script that is triggered when a submission is made with the spreadsheet form :
function onEntry(e){
Logger.log(e);
MailApp.sendEmail("scriptadmin#uniben.edu", "New Mail Request", "Someone submited data");
}
How can I reject the entry, say if it's a duplicate entry ?
Using the documentation on events you will have to choose what data you want check (user name, specific field...) and compare that to data already in the spreadsheet.
You should do these iterations on an array level since it will be far more efficient and fast, you can get data in an array using something like
var data = SpreadsheetApp.openById(key).getDataRange().getValues();
You could also use javascript function like indexOf() that will return -1 if no match if found or item position in the array if a match is found.
Actually there are many ways to do that but your question is too vague to know what will be the best...
EDIT : following your comment, I'd suggest you let the duplicate form data come into the sheet and then use a script to remove duplicates. You could run this script on a on form submit trigger or on a timer to let it run daily or hourly, and send the email only if the last entry was a new one (no duplicates found)... depending on your use case.
There is a script in the gallery that does the job pretty well, it was written by Romain Vialard, a GAS TC that has contributed a lot. (the link above goes to the script description but you can get it also in the public gallery, just search for 'remove duplicates' you'll see that other scripts do that, all the scripts in the gallery have been checked by the GAS team)
4 months late, but better late than never. I believe this function does almost what was originally requested. i.e. "How do I prevent the entry from entering the spreadsheet if I decide that it's a duplicate." It is not precisely what was requested, but very close.
This code checks one column against that same column in another sheet, for all rows in that sheet. Lets say you have a list of companies or clients on a sheet. That list includes name, phone, address, etc. etc. Lets say you want to check against the phone number - if the phone number you are currently entering is already on your client sheet, then don't allow entry - or more precisely clear it out immediately upon entering it.
I'm sure the more experienced members will be able to point out flaws, but it works for me.
I believe it will also work for the case where a phone number in the middle of the sheet is changed - so it's not just last line that gets checked, it's the line that gets edited that gets checked - I've not tested this particlar scenario. Also, I made some changes to variable names to protect the innocent...hopefully I didn't mess anything up while doing that.
I call this function from within another function that is triggered by onEdit. Theoretically it should be able to be installed as an onEdit trigger itself. I hope someone finds it useful.
function checkNewEntryForDuplicate(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entrySheet = SpreadsheetApp.getActiveSheet();
var clientSheet = ss.getSheetByName("Clients");
var r = entrySheet.getActiveCell();
var lastCol = entrySheet.getLastColumn();
// If this had any consistency, we'd be able to get the row from entrySheet the same
// as we get column. But there is no getRow() method at the sheet level.
var rowNum = r.getRow();
var clientData=clientSheet.getDataRange().getValues();
var phoneColumnOffset=getPhoneColumnOffset(); // You'll need to get the offset elsewhere. I have a function that does that.
var columnNum=e.range.getColumn(); // column that is currently being edited
if (columnNum != phoneColumnOffset+1) // no point in doing anything else if it's not the column we're interested in.
return 0;
var entryRow=entrySheet.getRange(rowNum, 1, 1, lastCol);
var phoneNum = e.range.getValue();
// iterate over each row in the clientData 2-dimensional array.
for(i in clientData){
var row = clientData[i];
var duplicate = false;
// For each row this conditional statement will find duplicates
if(row[phoneColumnOffset] == phoneNum){
duplicate = true;
var msg="Duplicate Detected. Please do not enter. Deleting it..."
Browser.msgBox(msg);
entryRow.clearContent();
entryRow.clearComment();
return duplicate;
}
}
return duplicate;
}
I am doing the same things but having no scripts at all and just by spreadsheet functions. That kind of things are just like SQL for me and very interest to do.
For your question, this link will help: http://www.labnol.org/software/find-remove-duplicate-records-google-docs/5169/

ABOUT: Tutorial: Sending emails from a Spreadsheet

Is it possible to get this script updated? In 2009 it may have worked, but it doesn't now.
Tutorial: Sending emails from a Spreadsheet -
Quick link to Google Developers Tutorial
I can't for the life of me get my own script to work. Having a problem incrementing the rows correctly when it checks before sending, which is either leaving me sending a dozen e-mails of a single row of data or if I try to implement a while loop, I've ended up sending myself over hundreds of e-mails and google then stops me from using the function any further until the next day.
My specific script question is HERE, except I don't think I worded it correctly because no one is replying.
It seems that you forgot a couple of things in your script :
1° : var dataRange = sheet.getRange(sRow,1,1,cols); // this gets only 1 row in your sheet so it is normal that the loop doesn't work (length=1).
I'd suggest to replace the height value by the last row value (see docs to get that value) to make the loop iterate through every rows.
2° when you use .setValue(EMAIL_SENT); the value of EMAIL_SENT is not defined in your code (it is defined outside the function in the tutorial).
I'd suggest to add a statement like this : var EMAIL_SENT="EMAIL_SENT" or, more simply use the string value in your 'setValue' statement like this : .setValue("EMAIL_SENT");