How to skip duplicate rows values between separate spreadsheets - javascript

I have two spreadsheets with different types of data. The only similarity is a list of names spread out across the spreadsheets.
I want to create a script that lets me copy specific rows from Spreadsheet 2 to Spreadsheet 1 with two conditions:
Only rows marked Ready in Spreadsheet 2 should be copied over to Spreadsheet 1
No rows containing names already in Spreadsheet 1 should be copied over from Spreadsheet 2
I'm probably missing something simple, but I can't get Point 2 to work.
function CopyClick() {
var sheet = SpreadsheetApp.getActiveSheet();
var originalnames = sheet.getRange("A1:A").getValues();
var otherss = SpreadsheetApp.openById("Spreadsheet 2 ID HERE");
var copysheet = otherss.getSheetByName("Spreadsheet 2 Sheet Name");
var copynames = copysheet.getRange("C1:C").getValues();
var statusdata = copysheet.getRange("D1:D").getValues(); //First condition has to be "Ready"
var copylastrow = copysheet.getLastRow();
for(var i=0; i<copylastrow; i++) {
if(statusdata[i][0].toString().match(/Ready/ig) && (copynames[i][0].indexOf(originalnames) === -1)){ //Don't want duplicates, but indexOf isn't working
var lastRow = sheet.getLastRow();
sheet.getRange(lastRow+1, 1).setValue(copynames[i][0]); //back to the original spreadsheet
}
}
}
As you can see, I wasn't sure of how to keep the second condition, ending up with a placeholder indexOf.
How could I best solve this?

Explanation:
You can take advantage of the includes() function. It returns true if there is a match. However, you need to flat() the originalnames array first. I also changed the range of originalnames to avoid taking empty cells:
var originalnames = sheet.getRange("A1:A"+sheet.getLastRow()).getValues().flat(1);
and inside the if condition use:
!originalnames.includes(copynames[i][0])
this will evaluate to true if copynames[i][0] is not included in originalnames array. I also replaced statusdata[i][0].toString().match(/Ready/ig) to statusdata[i][0] == 'Ready' but feel free to use your own version if this does not work.
Solution:
function CopyClick() {
var sheet = SpreadsheetApp.getActiveSheet();
var originalnames = sheet.getRange("A1:A"+sheet.getLastRow()).getValues().flat(1);
var otherss = SpreadsheetApp.openById("Spreadsheet 2 ID HERE");
var copysheet = otherss.getSheetByName("Spreadsheet 2 Sheet Name");
var copynames = copysheet.getRange("C1:C").getValues();
var statusdata = copysheet.getRange("D1:D").getValues(); //First condition has to be "Ready"
var copylastrow = copysheet.getLastRow();
for(var i=0; i<copylastrow; i++) {
if( statusdata[i][0] == 'Ready' && !originalnames.includes(copynames[i][0]) ){
var lastRow = sheet.getLastRow();
sheet.getRange(lastRow+1, 1).setValue(copynames[i][0]); //back to the original spreadsheet
}
}
}

Related

In app scripts, confirm that 2 adjacent cells match, then use a directory tab to push data to a specified sheet ID

Beware, I may be overthinking this.
I keep getting into cyclic thought loops when I'm trying to figure out what to do in this situation, So I will try to explain my thinking and where I am at.
Form is filled out on Google Sheets
Form replies are added to the main Form sheet in the "form responses tab"
Code actives, checking to see if the form was filled correctly (columns A and B match)
if they match, it finds the respective google spreadsheet ID that that row needs to go to, by looking at the directory tab.
That item is then sent over to the appropriate list, which is in it's own sheet
This continues for the rest of the rows of the Main QA Forms Responses tab, until all rows have been checked and there are no more entries.
I've been trying to understand this for hours on end, but might be approaching this all from the wrong angle.
As of right now, this is how far i've gotten in the code:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = s.getActiveRange();
var columnSearchNum = 3;
var columnDatastarts = "C";
var formSheetName = "QA Form Responses";
var directorySheetName = "Program Directory";
var matchingProgramSheetIDColumn = 1;
if(s.getName() == formSheetName && r.getColumn(0) == r.getColumn(1)) {
var sourceRow = r.getRow();
var matchingProgram = sourceRow.getRange(0,0).getValue();
var matchingProgramSheetID = s.getName(Programdirectory)....//[code needed1]
//^^^^ I need a line here to pull matching the data inSheetID column where Matching program's string is
//from this code line, Go to that 'program's sheet'
var programSheet = ss.getSheetByID(matchingProgramSheetID);
var programSheetNumRows = programSheet.getLastRow();
//console.log(programSheetNumRows);
var formSheetNumColumns = s.getLastColumn();
var targetRange = programSheet.getActiveRange()
var targetValue = +s.getRange(columnDatastarts+sourceRow).getValue()
//console.log(targetValue);
var programSheetRange = programSheet.getRange(1,columnSearchNum,programSheetNumRows,1);
//console.log(programSheetRange.getNumRows() +" " +programSheetRange.getNumColumns() + " " + programSheetRange.getValues());
var targetRow = findIndex(programSheetRange.getValues(), targetValue);
//console.log(targetRow);
var target = programSheet.getRange(targetRow, 1);
s.getRange(sourceRow, 2, 1, formSheetNumColumns).moveTo(target);
;
}
}
function findIndex(array, search){
//console.log(array);
if(search == "") return false;
for (var i=0; i<array.length; i++){
//console.log("comparing " + +array[i] + " to "+ +search);
if (+array[i] == +search){
return i+1;
}
}
return -1;
}
You want to find a match for a value on the Form Responses sheet with values in a given column on another sheet, and then return the value in the cell adjacent to the matching cell.
There are probably many ways to do this, but the Javascript method indexOf is an obvious choice if using a script. The following is untested, but the logic is sound.
Insert at [code needed1]
// define the Program sheet
var progsheet = ss.getSheetByName(directorySheetName)
// define the first row of data on the program sheet
var firstRowofData = 3
// get the data for columns one and two of the program sheet.
// starting row=3, starting column=1, number of rows = lastrowminus first row plus 1, number of columns = 2
var progsheetdata = progsheet.getRange(firstRowofData,1,progsheet.getLastRow()-firstRowofData+1,2).getValues()
// get Program manager Column
var ProgManager = progsheetdata.map(function(e){return e[0];})
// search the Program manager Column for the first instance of the matching program value
// indexOf returns index if found, or -1 if not found
var result =ProgManager.indexOf(matchingProgram);
if (result == -1){
// couldn't find a matching program
// do something to quit/error message
return
}
else
{
// the id will be in the row returned by IndexOf and in the adjacent column to the right.
var id = progsheetdata[result][1]
}

Google Apps Script is taking too much time, is there a way to reduce the runtime?

function dataManp() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("My-Sheet-1");
var pasteSheet = ss.getSheetByName("My-Sheet-2");
var clearContentRange = pasteSheet.getRange("A1:Z100");
clearContentRange.clearContent();
var source = copySheet.getRange("a1:f100");
var destination = pasteSheet.getRange("a1:f100");
source.copyTo(destination, {formatOnly:true , contentsOnly:true});
source.copyTo(destination,SpreadsheetApp.CopyPasteType.PASTE_COLUMN_WIDTHS,false);
var rows = pasteSheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[3] == '') {
var deleteRowNum = (parseInt(i)+1) - rowsDeleted
pasteSheet.deleteRow(deleteRowNum);
// temp_array[i] = i
rowsDeleted++;
}
}
pasteSheet.deleteColumn(2)
}
Hi,
I have written the following script to perform the following operations,
Copy data from My-Sheet-1 and paste to My-Sheet-2
Deletion of rows that corresponds to empty cells in column 3.
After that deletion of column 2
Rigthnow, the My-Sheet-1 contains only 60 rows and 20, the script is taking approximately 7 secs to complete. However in future the number of rows may extend to some 1000. Is there a way to optimize the above code, so that it takes less time to complete.
My observation is that, copy and pasting the data takes just milli secs. The major time consuming part are the operations, I am performing in the pasteSheet after pasting it. It may be helpful, if we can copy My-Sheet-1 to a temporary variable (copy everything including the formulas, format specifications, values, text etc..) and perform all operations in the temporary variable and then paste everything in the temporary variable to the desired target sheet. But, I don't know, how to copy everything in a sheet to a temporary variable, also, I am not sure, whether this will reduce the time or not. I would be glad, if I can get some help on this as well (i.e. copying everything in a sheet to a temporary variable, perfrom operations on the variables and then paste data in the variable to a new sheet)
Thank you
Edit - 1
Would like to add that, My-Sheet-1 contains mixed data (i.e. numerics, color formatted text, formulas in some cells etc)
Explanation:
deleteRow() takes some time per execution, so it's not recommended to use on hundreds of rows in a loop.
Simple answer would be:
Make a 2D array for Sheet1 using getValues().
Delete / filter out array elements depending if row2 is blank.
Use setValues() to write the filtered array into Sheet2.
Sample Code:
function dataManp() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("My-Sheet-1");
var pasteSheet = ss.getSheetByName("My-Sheet-2");
var lr = copySheet.getLastRow();
var clearContentRange = pasteSheet.getRange(1,1,lr,26);
clearContentRange.clearContent();
var source = copySheet.getRange(1,1,lr,6);
var destination = pasteSheet.getRange(1,1,lr,6);
source.copyTo(destination, {formatOnly:true , contentsOnly:true});
source.copyTo(destination,SpreadsheetApp.CopyPasteType.PASTE_COLUMN_WIDTHS,false);
destination.clearContent();
var values = source.getValues();
var temp_array = [];
for (var i = 0; i < lr; i++) {
var rowValue = values[i];
if (rowValue[2] != '') {
temp_array.push(rowValue);
}
}
var newDest = pasteSheet.getRange(1,1,temp_array.length,6)
newDest.setValues(temp_array);
pasteSheet.deleteColumn(2);
}
One caveat is that you need to have the same format for all rows in a column.
Sample Input:
Sample Output:

How to copy and set a row of data into another sheet if a string condition is met in a column

I've been trying to create a Submit sheet that has 42 rows. They have a border around it indicating where I type my data in. My goal is once Submit is typed in at the end of the row it'll paste that row of data into the bottom of my Database sheet
Here is the code ive been writing and having problems with. Sorry if it looks whacky I'm rookie at this
function submit1(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet");
var range = sheet.getDataRange();
var colVal = "Submit";
var colToSearch = 19;
var dataRangeVals = range.getValues();
for(var i = dataRangeVals.length; i <= 173; i++){
if([i][colToSearch] === colVal){
var sourceVal = sheet.getRow(i+[colToSearch]).getValues();
Logger.log(sourceVal);
var targetSheet = ss.getSheetByName("DataBase");
var lastRow = targetSheet.getLastRow();
targetSheet.getRange(lastRow + 1,1,1,18).setValues(sourceVal);
};
};
};
the var dataRangeVals works and gets all the values of my submit sheet, but when I iterate through it and log sourceVal to check if it grabbed the row of data when I typed Submit into Column T
No values show in the execution log which is probably why nothing gets pasted into my DataBase sheet but it still shows execution complete without any errors which is confusing me . The problem is I don't understand why that's happening.
If you guys could help me out and take a look at it i'd greatly appreciate it.
Here is the link if you'd like it.
Stock Database updated link
Issues:
for(var i = dataRangeVals.length; i <= 173; i++){ will start at 131 and no data will be captured. Instead, replace it with for(var i = 0; i < dataRangeVals.length; i++){.
[i][colToSearch] wont return anything. To read the element of an array it should be dataRangeVals[i][colToSearch].
I tried to print the column T of your sheet and the Submit text has extra space in it. if([i][colToSearch] === colVal){ won't work because Submit is not equal to Submit . Instead use String.match().
sheet.getRow(i+[colToSearch]) wont work because Class Sheet has no getRow() method. Replace getRow() with getRange(row, column, numRows, numColumns)
Code:
function submit1(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet");
var range = sheet.getDataRange();
var colVal = "Submit";
var colToSearch = 19;
var dataRangeVals = range.getValues();
for(var i = 0; i < dataRangeVals.length; i++){
if(dataRangeVals[i][colToSearch].toString().match(colVal)){
var sourceVal = sheet.getRange(i+1, 1, 1, 19).getValues();
var targetSheet = ss.getSheetByName("DataBase");
var lastRow = targetSheet.getLastRow();
sourceVal[0].splice(0,1)//remove the empty first element of subarray
targetSheet.getRange(lastRow + 1,1,1,18).setValues(sourceVal);
};
};
};
Output:
I tested your code and found that on your IF condition if([i][colToSearch] === colVal), the [i][colToSearch] has a null or undefined value as seen here on the execution log result. Thus, your IF statement automatically ends the execution of your code and no values are shown.
I did some tweaks on your code to be able to proceed on the IF statement (to find if the last row matches the word "Submit") then copy the last row data from "Sheet" to the "DataBase" sheet's last row.
Please refer to my sample code here: Updated
function submit1(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet");
var range = sheet.getDataRange();
var targetSheet = ss.getSheetByName("DataBase");
var lastRow = targetSheet.getLastRow()+1;
var colVal = "Submit";
var colToSearch = 20;
var dataRangeVals = range.getValues();
for(var i = 1; i <= dataRangeVals.length; i++){//Loop to check column 20 with "Submit" value
if(sheet.getRange(i,colToSearch).getValue().valueOf(colVal)){//Check each rows on column 20 to find ones that has "Submit" value
for(var x = 2; x<=colToSearch-1; x++){//When a row has "Submit" value, it will get all row data and copies it to "DataBase" sheet
var sourceVal = sheet.getRange(i,x).getValues();
targetSheet.getRange(lastRow,x-1).setValues(sourceVal);
}
lastRow = targetSheet.getLastRow()+1;//Refreshes the new last row on "DataBase" sheet
Logger.log("Done copying row data on \"Sheet\" Row #" + i + " to \"DataBase\"");
}else{
//Do nothing
}
}
}
After running the code, you will be able to see all of the data from the row on "Sheet", copied and added on every last row of the "Database" Sheet. You Should this sample execution log result once it is successful.
Updated Result
Here's my test spreadsheet, based on your spreadsheet design:
Here's the result on DataBase sheet:

Comparing two columns, same row, for first value that doesn't match

I have two sheets, one is a mirror sheet, "Sheet2," that I use to store the values of the other sheet, "Sheet1." My goal is to have a function compare the two sheets for differences. The best way i could think of was by comparing column A from Sheet1 to column A from Sheet2. I found a few functions that compared 2 columns but it did it looking for values from one column and finding it in the other column. Or by returning all the values in those cells that had a matching value, regardless of what row it was in. But I don't want the values in the cells, necessarily. I want to find the first row where the two columns stop matching. I'm fairly new to Javascript so I still can't comprehend the whole for (var j = 0; j < range.length; j++) stuff.
But I'm sure I will need to know how to use it for this function I need. Here's what I tried using but instead of giving me row ranges, it gave me an array of values that were the same, if I changed it to if(lookup[i][0]!==range[j][0]){ it gave me all the possible combinations that weren't matching. This is from stackoverflow.com/questions/42044903
function findDifference() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s=ss.getSheetByName("Sheet1")
var lr=s.getLastRow()
var lookup = s.getRange(2,1,lr-1,2).getValues();
var s1=ss.getSheetByName("Sheet2")
var lr1=s1.getLastRow()
var range = s1.getRange(2,1,lr1-1,2).getValues();
var lookupRange = [];
for (var i = 0; i < lookup.length; i++) {
for (var j = 0; j < range.length; j++) {
var test=lookup[i][0]
if(lookup[i][0]!==range[j][0]){
lookupRange.push([range[j][0],range[j][1],lookup[i][0],lookup[i][1],]);
}}}
s1.getRange(10,1,lookupRange.length,4).setValues(lookupRange);
}
I feel like there's a very similar function for what I'm trying to do that already exists, but I can't seem to find it or come up with how it would work because I'm new and don't know all the tricks.
Something like:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1=ss.getSheetByName("Sheet1")
var s2=ss.getSheetByName("Sheet2")
var ColumnA1 = s1.getRange('A:A').getValues()
var ColumnA2 = s2.getRange('A:A').getValues()
var Row = Function()
///Some function I can't think of using where
if(ColumnA1 + Row !== ColumnA2 + Row){
???.getRow()
}
The code that you had was "kinda' helpful but it did not solve your particular question. On the other hand, your if(ColumnA1 + Row !== ColumnA2 + Row){ wasn't really helpful either.
Regrettably you DO need to "comprehend the whole for (var j = 0; j < range.length; j++) stuff", though it isn't actually that complicated.
In the following answer, there are basically three elements.
setup sheet1, and get the data
setup sheet2, and get the data
loop through the rows and compare the value on a given line from one sheet to the other.
the for statement signifies the loop
i is simply a counter variable
i=0 means that the starting value is zero. In javascript arrays, zero always the first value set.
i < Sheet1Data.length signifies how many time the loop will run. In this case, it will run while i is less then the number of lines in the array. Remember, i starts with zero, so "less than" the totoal number of lines will be fine.
i++ means that each time the code loops, it increments i by one.. So, i starts with 0, then 1, 2, 3 and so on.
How to find the first row where the two columns stop matching
View the Logs (View > Logs).
You can see on line 32 and 38 of the code Logger.log statements. These record the line number and whether the line values in each sheet match.
function so56195933() {
// setup Spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// setup Sheet1
var s1 = ss.getSheetByName("Sheet1")
var s1LastRow = s1.getLastRow();
//Logger.log("DEBUG: Sheet 1 Last row = "+s1LastRow);
var Sheet1DataRange = s1.getRange(1,1,s1LastRow);
var Sheet1Data = Sheet1DataRange.getValues();
//Logger.log("DEBUG: Sheet 1 data range = "+Sheet1DataRange.getA1Notation());
var Sheet1length = Sheet1Data.length;
//Logger.log("DEBUG: Sheet1 length = "+Sheet1length);
// setup Sheet2
var s2=ss.getSheetByName("Sheet2")
var s2LastRow=s2.getLastRow();
//Logger.log("DEBUG: Sheet 2 Last row = "+s2LastRow);
var Sheet2DataRange = s2.getRange(1,1,s2LastRow);
var Sheet2Data = Sheet2DataRange.getValues();
//Logger.log("DEBUG: Sheet 2 data range = "+Sheet2DataRange.getA1Notation());
var Sheet2length = Sheet2Data.length;
//Logger.log("DEBUG: Sheet2 length = "+Sheet2length);
// Loop through rows compare value per each sheet
for (var i = 0; i < Sheet1Data.length; i++) {
var s1data = Sheet1Data[i][0];
var s2data = Sheet2Data[i][0];
//Logger.log("DEBUG: Line: "+i+", s1data: "+s1data+" Vs s2data: "+s2data);
if (s1data !=s2data){
// sheets values don't balance
Logger.log("Line: "+i+". Sheets are NOT equal. Sheet1 = "+s1data+", Sheet2 = "+s2data);
return false;
}
else
{
// sheets values balance
Logger.log("Line: "+i+". Sheets are equal, value: "+s1data);
}
}
}
This is my test data

Delete Row in Google Sheets Based on Cell Text (if contains)

I am trying to create a script that will automatically delete the row if the cell contains, but does not exactly match, the condition. For example, the script would delete the rows if the cell contains gmail.com, as part of a larger email
123#gmail.com
123#yahoo.com
456#gmail.com
456#yahoo.com
The two yahoo emails would be saved because they do not meet the condition. However, I am unsure of the proper conditional dictation to use when writing my script. Here is what I have so far.
function deleteRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Sheet1');
var r = s.getRange('B:B');
var v = r.getValues();
for(var i=v.length-1;i>=0;i--)
if(v[0,i] 'gmail.com')
s.deleteRow(i+1);
};
I would put a ==, but this will only delete rows that match exactly with gmail.com. What would I use in place of the == to make it essentially a text contains condition?
Try this:
function deleteMatchingRows(string) {
var string=string||'gmail.com';
var ss = SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var lngth=sh.getLastRow();
var rg=sh.getRange(1,2,lngth,1);
var vA=rg.getValues();
var n=0;
for(var i=1;i<vA.length;i++){
if(vA[i][0] && vA[i][0].indexOf(string)>-1){
sh.deleteRow(i-n+1);
n++;
}
}
}

Categories