Google Apps Script mysteriously stops functioning in for loop - google-apps-script

I am building a system to copy data from one sheet to another sheet (On a different spreadsheet). It works well, until it is supposed to copy the data. Afterward, it does nothing. No error is thrown, but no alert boxes pop up in the for loop. Here is my code:
function onEdit() {
var sheet = SpreadsheetApp.getActiveSheet();
var master = SpreadsheetApp.openById('13x7AvyYTaocCVBxVZ3ckBjzgxcZfjg9RYM1cE_0VNbU');
var sheets = master.getSheets();
var test = DriveApp.createFile('Test', 'fetching user data...');
var name = test.getOwner().getName();
test.setTrashed(true);
var add = true;
for (var i = 0, n; i < sheets.length; i++) {
n = sheets[i];
if (n.getName() == name) {
add = false;
updateSheet(n, name);
}
}
if (add) {
newSheet(master, name);
}
}
function updateSheet(m, name) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var nr = range.getNumRows();
var nc = range.getNumColumns();
for (var i = 1; i <= nr.length; i++) {
for (var n = 1, s; n <= nc.length; n++) {
s = range.getCell(i, n);
m.getRange(i, n).getCell(1, 1).setValue(s.getValue());
}
}
}
function newSheet(master, name) {
var m = master.insertSheet(name);
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var nr = range.getNumRows();
var nc = range.getNumColumns();
for (var i = 1; i <= nr.length; i++) {
for (var n = 1, s; n <= nc.length; n++) {
s = range.getCell(i, n);
m.getRange(i, n).getCell(1, 1).setValue(s.getValue());
}
}
}
I have searched the code, but can't find anything wrong.

On google documentation at https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#insertsheetsheetname, it says ...
insertSheet(sheetName)
Inserts a new sheet into the spreadsheet with the given name. The new sheet becomes the active sheet.
In your code
function newSheet(master, name) {
var m = master.insertSheet(name); // after calling this function m becomes the active sheet
var sheet = SpreadsheetApp.getActiveSheet(); // you've assigned m to sheet
I would recommend using getSheetByName('SheetName').
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SheetName");
Refer to this link https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getsheetbynamename

The obvious answer (Thanks #Rubén)
In the for loop, it says nc.length and nr.length. Both getNumRows and getNumColumns return numbers. Since these are integers, .length is not necessary.

Related

How can Google Sheets Form Update Records from Results Using Google App script?

I have a program that filters and updates data from an existing sheet.
The program works as follows:
1. Find and filter out the required value
2. Enter data in [Adjustment] column then update to database in Record sheet.
I tried to try but my program doesn't seem to work.
I tried to edit the program code but when run it will affect the other columns and the [adjustment] column value is entered wrong.
This is my link program
function Searchold(){
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var shtRecords = ss. getSheetByName ("RECORD");
var shtForm = ss. getSheetByName ("TEST") ;
var records = shtRecords. getDataRange () . getValues ();
var sField = shtForm. getRange ("A3").getValue ();
var sValue = shtForm.getRange ("A6").getValue();
var sCol = records [0].lastIndexOf(sField);
var results = records.filter(function(e){return sValue == e[sCol] });
if(results.length==0){SpreadsheetApp.getUi().alert("not found values");}
else{
shtForm.getRange(9,1,results.length,results[0].length).setValues(results);
}
}
function Updatenew(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var shtRecords = ss.getSheetByName("RECORD");
var shtForm = ss.getSheetByName("TEST");
var LastRow = shtForm.getRange("A8").getNextDataCell(SpreadsheetApp.Direction.DOWN).getLastRow();
var newData = shtForm.getRange(9,1,LastRow -1,7).getValues();
for(var i =0; i<newData.length;i++){
var oldData= shtRecords.getDataRange().getValues();
for(var j= 0;j<oldData.length;j++){
if(newData[i][0] ==oldData[j][0]){
var newData2 = [newData[i]];
shtRecords.getRange(j + 1,1,1,newData2[0].length).setValues(newData2);
}
}
}
}
Can you help me with the update program? Sincerely thank you
Modification points:
When I saw your showing script of Updatenew, I think that each row of var oldData = shtRecords.getDataRange().getValues() is used in each loop of for (var i = 0; i < newData.length; i++) {}. By this, each row is overwritten by each row of newData. By this, all searched rows in "RECORD" sheet are the same value. I thought that this might be the reason for your issue.
var oldData = shtRecords.getDataRange().getValues(); can be used one call.
In order to avoid this issue by modifying your script, as one of several methods, how about the following modification?
From:
for (var i = 0; i < newData.length; i++) {
var oldData = shtRecords.getDataRange().getValues();
for (var j = 0; j < oldData.length; j++) {
if (newData[i][0] == oldData[j][0]) {
var newData2 = [newData[i]];
shtRecords.getRange(j + 1, 1, 1, newData2[0].length).setValues(newData2);
}
}
}
To:
var oldData = shtRecords.getDataRange().getValues();
for (var j = 0; j < oldData.length; j++) {
for (var i = 0; i < newData.length; i++) {
if (newData[0][0] == oldData[j][0]) {
var newData2 = newData.splice(0, 1);
shtRecords.getRange(j + 1, 1, 1, newData2[0].length).setValues(newData2);
break;
}
}
}
Note:
At the above modification, setValues is used in a loop. In this case, the process cost becomes high. If you want to reduce the process cost of the script, how about using Sheets API? When Sheets API is used, how about the following modification? Please enable Sheets API at Advanced Google services.
To
var temp = newData.slice();
var data = shtRecords.getDataRange().getValues().reduce((ar, r, i) => {
if (temp[0][0] == r[0]) {
var t = temp.splice(0, 1);
t[0][2] = Utilities.formatDate(t[0][2], Session.getScriptTimeZone(), "dd/MM/yyyy");
t[0][4] = Utilities.formatDate(t[0][4], Session.getScriptTimeZone(), "dd/MM/yyyy");
ar.push({ range: `'RECORD'!A${i + 1}`, values: t });
}
return ar;
}, []);
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, ss.getId());

How to Collect data from ALL sheets using google Appscript

I have script that searches through an entire workbook for a specific name and returns all the data on that name. The script works, but only collects data from 1 sheet within the workbook.
I searched for some code to assist me getting all the sheet names. So I have code that does that, but for some reason it still only returns from 1 sheet.
The code below collects all the sheet names.
This function is then called in the query function.
I Suspect that this is where the issue is occuring
function sheetnames() {
var out = new Array()
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var i=0 ; i < sheets.length ; i++) {
var name = sheets[i].getName();
var data = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
var values = data.getRange(4, 1, data.getLastRow(),
data.getLastColumn()).getValues();
out.push(values);
}
return out;
}
This function then searches for the requested data.
function query() {
var Sheet = SpreadsheetApp.getActiveSpreadsheet();
var searchSheet = Sheet.getSheetByName("Search");
var searchByName = searchSheet.getRange(4, 8).getValue();
var uses = sheetnames();
var output = new Array();
var i = 0;
var r = 0;
do{
var from = uses[i];
do{
var row = from[r];
if(row == null){
r++;
continue;
}
if(searchByName != null ){
var newName = row[7];
if(newName == searchByName){
output.push(row);
}
}
r ++;
}while(r < from.length);
i ++;
}while(i < uses.length);
return output;
}
This part just prints the data into the cells and is attached to a search drawing, which runs the function in the sheet.
function search() {
var Sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Search");
var data = query();
var count1 = 0;
do{
var subData = data[count1];
var count2 = 0;
do{
var setTo = subData[count2];
Sheet.getRange((count1 + 5), (count2 + 1)).setValue(setTo);
count2 ++;
}while(count2 < subData.length);
count1 ++;
}while(count1 < data.length);
}
The sheet is called the "Daily Payments Sheet." As you can imagine there is A LOT of data. Each sheet name is named by the month and the year that the payment occurred. The more consistent customers would obviously make purchases in more than one month.
So when searching for a customers name, I only get 1 month (1 sheet's data) returned. We have data from May 2018 till date, so again, the script doesn't collect from all the sheets.
Your code is not very readable so I figured some things on my own and simplified it. Things I assume - your search term is in 'Search' sheet column H4 and you want to search all sheets for this term in H4 column and write those out in 'Search' sheet after 4th row. Try this.
// return all rows from all sheets except Search sheet
function sheetValues(ss) {
var out = [];
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
var sheet = sheets[i];
if (sheet.getName() == 'Search') continue;
var values = sheet.getRange(4, 1, sheet.getLastRow() - 3, sheet.getLastColumn()).getValues();
out.concat(values);
}
return out;
}
// search all rows for given term and return results
// look for term in H column of every row
function query(ss, term) {
if (!term) return;
var values = sheetValues(ss);
var output = [];
for (var i = 0; i < values.length; i++) {
var row = values[i];
var name = row[7]; // 7 = col H
if (name == term) {
output.push(row);
}
}
return output;
}
// get search results and print into Search sheet
function search() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Search');
var searchByName = sheet.getRange(4, 8).getValue(); // search term is in H4 cell
var data = query(ss, searchByName);
sheet.getRange(5, 1, sheet.getLastRow() - 4, sheet.getLastColumn()).clearContent();
sheet.getRange(5, 1, data.length, data[0].length).setValues(data);
}

Merging google sheets: match with specific col values and merge data in 2 different sheets

I would like to ask if there is any possible way to set up a simple version of app script that is similar to the below add-ons.
https://chrome.google.com/webstore/detail/merge-sheets/gdmgbiccnalapanbededmeiadjfbfhkl?utm_source=ablebits
Basically, I've got sheet 1 and sheet 3, both sheets will have a common key column with specific values in each cell, and I would like to map both sheets with the data in that column then update the whole row data in the other sheets (For example, if i updated the sheet 3 then it map that col value in sheet 1, then paste the data in the corresponding row)
I have came up with a code that runs but no changes have been made, could anyone please advise how can I modify it to a simple version similar to the above add-ons? Thanks in advance.
I would like to populate the date from sheet 3 to sheet 1 after the code run, while the data from col C is matched in both sheets, please see example below, thanks!
For example, the data in sheet 1 highlighted row is having its key col with col C for common lookup value with sheet 3 while the row sequence is diff with sheet 3 (Please see the next photo, thanks!)
As you can see in sheet 3, the data of the whole row is inserted to the correct row according to the col C key col value which matched with sheet 1.
function myFunction2(){
// Get your spreadsheet and the sheets "TB" and "2"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet 1");
var sheet3 = ss.getSheetByName("Sheet 3");
// Get the range on sheet "TB"
var tabsheet1 = sheet1.getRange(2, 2, sheet1.getLastRow(),
sheet1.getLastColumn());
// Get the values to compare
var datasheet1 = tabsheet1.getValues();
var datasheet3 = sheet3.getRange(2, 2, sheet3.getLastRow(), sheet3.getLastColumn());
for(var i = 0; i < datasheet1.length; i++){
for(var j = 0; j < datasheet3.length; j++){
// Compare data: if they're the same, put the value
if(datasheet1[i][0]=(datasheet3[j][0]) == 0){
//if(datasheet1[i][0].localeCompare(datasheet3[j][0]) == 0){
datasheet1[i][1] = datasheet3[j][1];
}
}
}
// Take the modified tab and put it on the spreadsheet
tabsheet1.setValues(datasheet1);
}
You want to copy the data of sheet 3 to sheet 1. For this situation, when the values of column C of sheet 3 and sheet 1 is the same, you want to copy the row of sheet 3 to sheet 1, because the order of rows for sheet 3 and sheet 1 are different. If my understanding is correct, how about this modification?
Modification points :
In order to retrieve the values from column C to last column, it uses getRange(2, 3, sheet1.getLastRow(), sheet1.getLastColumn()).
In your script, datasheet3 is the range.
About if(datasheet1[i][0]=(datasheet3[j][0]) == 0){, in order to comare the date, I used getDisplayValues().
By this, the values retrieved as a string are compared.
Modified script :
function myFunction2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet 1");
var sheet3 = ss.getSheetByName("Sheet 3");
var tabsheet1 = sheet1.getRange(2, 3, sheet1.getLastRow(), sheet1.getLastColumn());
var datasheet1 = tabsheet1.getValues();
var datasheet1d = tabsheet1.getDisplayValues(); // Added
var tabsheet3 = sheet3.getRange(2, 3, sheet3.getLastRow(), sheet3.getLastColumn()); // Added
var datasheet3 = tabsheet3.getValues(); // Modified
var datasheet3d = tabsheet3.getDisplayValues(); // Added
for (var i = 0; i < datasheet1.length; i++) {
for (var j = 0; j < datasheet3.length; j++) {
if (datasheet1d[i][0] == datasheet3d[j][0]) { // Modified
datasheet1[i] = datasheet3[j]; // Modified
}
}
}
tabsheet1.setValues(datasheet1);
}
Reference :
getDisplayValues()
If I misunderstand your question, please tell me. I would like to modify it. At that time, in order to modify, can you share the sample sheet? Of course, please remove your private information from it.
Edit 1 :
function myFunction2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet 1");
var sheet3 = ss.getSheetByName("Sheet 3");
var tabsheet1 = sheet1.getRange(2, 3, sheet1.getLastRow() - 1, sheet1.getLastColumn() - 2); // Modified
var datasheet1 = tabsheet1.getValues();
var datasheet1d = tabsheet1.getDisplayValues(); // Added
var tabsheet3 = sheet3.getRange(2, 3, sheet3.getLastRow() - 1, sheet3.getLastColumn() - 2); // Added
var datasheet3 = tabsheet3.getValues(); // Modified
var datasheet3d = tabsheet3.getDisplayValues(); // Added
for (var i = 0; i < datasheet1.length; i++) {
for (var j = 0; j < datasheet3.length; j++) {
if (datasheet1d[i][0] == datasheet3d[j][0]) { // Modified
datasheet3[j].push(""); // Modified
datasheet1[i] = datasheet3[j]; // Modified
}
}
}
tabsheet1.setValues(datasheet1);
}
Edit 2 :
function myFunction2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("comment_log");
var sheet3 = ss.getSheetByName("CM");
var tabsheet1 = sheet1.getRange(2, 3, sheet1.getLastRow() - 1, sheet1.getLastColumn() - 2); // Modified
var datasheet1 = tabsheet1.getValues();
var datasheet1d = tabsheet1.getDisplayValues(); // Added
var tabsheet3 = sheet3.getRange(2, 3, sheet3.getLastRow() - 1, sheet3.getLastColumn() - 2); // Added
var datasheet3 = tabsheet3.getValues(); // Modified
var datasheet3d = tabsheet3.getDisplayValues(); // Added
for (var i = 0; i < datasheet1.length; i++) {
for (var j = 0; j < datasheet3.length; j++) {
if (datasheet1d[i][0] == datasheet3d[j][0]) { // Modified
if (datasheet1[i].length != datasheet3[j].length) {
for (var k = 0; k < datasheet1[i].length - datasheet3[j].length; k++) {
datasheet3[j].push(datasheet1[i][datasheet1[i].length - 1]);
}
}
datasheet1[i] = datasheet3[j]
}
}
}
tabsheet1.setValues(datasheet1);
}

GetCell -> GetValue -> SetValue fails

I want to make the below mentioned code working but it doesn't - nothing happens when I run it (also no error), that means the username (sUserName) doesn't get saved in the spreadsheet... And also I don't understand why the columns cant start with 2, 3, 4 (then the timestamp can stay in column #1) instead 1, 2, 3 - if so I get an error.
Here is the code:
var userNameColumn = 1; //Column where the user name is written
var subTypeColumn = 2; //Column where the submitter type is written ex. "Requester"
var sUserNameColumn = 3; //Column where the user name is saved
function saveUserName() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
for (var i = 1; i <= numRows; i++) {
var userNameCell = rows.getCell(i, userNameColumn);
var subTypeCell = rows.getCell(i, subTypeColumn);
var sUserNameCell = rows.getCell(i, sUserNameColumn);
if(sUserNameCell.isBlank() && subTypeCell.getValue() === 'Requester') {
sUserNameCell.setValue(userNameCell)
};
}
}
Here is the link for my spreadsheet and code:
Google Spreadsheet
See if this helps
function saveUserName() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var rows = sheet.getDataRange().getValues();
for (var i = 1; i < rows.length; i++) {
var userNameCell = rows[i][1];
var subTypeCell = rows[i][2];
var sUserNameCell = rows[i][3];
if (!sUserNameCell && subTypeCell === 'Requester') sheet.getRange(i+1,4).setValue(userNameCell)
}
}

Delete Duplicate Row in Google Spreadsheet

I need this script to delete the row (within the Registration sheet) with the matching Registration Code to the Cancel Registration sheet's Registration code. As of now, this script only deletes a row if "sheetR.deleteRow(i);" is not inside "if (regCodeR === regCodeCR) {}". It doesn't delete the correct row either.
function rD() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetR = ss.getSheetByName("Registration");
var sheetCR = ss.getSheetByName("Cancel Registration")
var dataR = sheetR.getDataRange().getValues();
var dataCR = sheetCR.getDataRange().getValues();
var headerRow = 1;
for (var i = 1; i in dataR && i in dataCR; ++i) {
var rowR = dataR[i];
var rowCR = dataCR[i];
var duplicate = false;
var regCodeR = sheetR.getRange(headerRow + i, 10).getValues();
var regCodeCR = sheetCR.getRange(headerRow + i, 9).getValues();
if (rowR[9] === rowCR[8]) {
duplicate = true;
}
}
if (regCodeR === regCodeCR) {
sheetR.deleteRow(i);
}
}
I tried this code with simple data having one column with registration numbers. You can modify the main sheet range according to your data. Also make sure to change the sheet names.
Tried and tested the code below :
function deleteDup(){
var mainSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('regis');
var cancelSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('cancel');
var mainSheetValues = mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).getValues();
var cancelSheetValues = cancelSheet.getRange(2, 1, cancelSheet.getLastRow(),1).getValues();
var k = 0;
var del = [];
// Getting the row index of matching values
for (i = 0; i < cancelSheetValues.length; i++)
{
var cancelValue = cancelSheetValues[i][0];
Logger.log(cancelValue);
for (j = 0; j < mainSheetValues.length; j++)
{
if(mainSheetValues[j][0] == cancelValue){
del[k] = j;
k++;
}
}
}
del.sort();
var count =0;
// deleting the values from the main sheet values array
for (i = 0; i < k; i++ )
{
mainSheetValues.splice((del[i] - count), 1);
count++;
}
var len = mainSheetValues.length;
// Update the sheet with new values
mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).clearContent();
mainSheet.getRange(2, 1, len,1).setValues(mainSheetValues);
}
But instead you can also do this way:
Set a flag value in the registration sheet if it matches with the cancellation sheet.
Then loop through the registration sheet data and delete the matching flag row.
Hope that helps!