Match two rows in google script - javascript

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++;
}
}
}

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

Changing existing script for Finding Duplicates in Google Sheets and entering "yes" in the last column instead of coloring it

I am new here and have no idea what I am doing. I found a script online and thought it would work for what I needed, but I would like to change it some. Any help would be appreciated. I have already changed it a little. If it would help to share the form with someone I can do that since it is only in the testing stages.
What I am wanting to do is instead of coloring the duplicates red in the main sheet I want it to put "Yes" in the last column of the main sheet. I would also like it to ignore the blank rows. Is this possible? (I copied the script I am currently using below)
function findDuplicates() {
// List the columns you want to check by number (A = 1)
var CHECK_COLUMNS = [13];
// Get the active sheet and info about it
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("USE THIS SHEET FOR DATA").activate();
var numRows = sourceSheet.getLastRow();
var numCols = sourceSheet.getLastColumn();
// Create the temporary working sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var newSheet = ss.insertSheet("FindDupes");
// Copy the desired rows to the FindDupes sheet
for (var i = 0; i < CHECK_COLUMNS.length; i++) {
var sourceRange = sourceSheet.getRange(1,CHECK_COLUMNS[i],numRows);
var nextCol = newSheet.getLastColumn() + 1;
sourceRange.copyTo(newSheet.getRange(1,nextCol,numRows));
}
// Find duplicates in the FindDupes sheet and color them in the main sheet
var dupes = false;
var data = newSheet.getDataRange().getValues();
for (i = 1; i < data.length - 1; i++) {
for (j = i+1; j < data.length; j++) {
if (data[i].join() == data[j].join()) {
dupes = true;
sourceSheet.getRange(i+1,1,1,numCols).setFontColor("red");
sourceSheet.getRange(j+1,1,1,numCols).setFontColor("red");
}
}
}
// Remove the FindDupes temporary sheet
ss.deleteSheet(newSheet);
// Alert the user with the results
if (dupes) {
Browser.msgBox("Possible duplicate(s) found and colored red.");
} else {
Browser.msgBox("No duplicates found.");
}
};

Service invoked too many times in a short time

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++;
}
}
}
}
};

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.

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