Sync row data based on unique ID in google sheets scripts - javascript

I currently have a code that does something very similar to this, but im not sure the small change i need to make to have it work correctly. Right now, the code below compares two rows of unique IDs and if the IDs are the same, it copies the cell in the "Comments" column to the other sheet.
function setComments() {
var ss = SpreadsheetApp.getActive(),
compare1 = "", compare2 = "",
outputSheet = ss.getSheetByName("Sheet2"),
sourceSheet = ss.getSheetByName("Sheet1"),
range1 = outputSheet.getDataRange(),
range2 = sourceSheet.getDataRange(),
lastCol1 = range1.getNumColumns(),
lastCol2 = range2.getNumColumns(),
values1 = range1.getValues(),
values2 = range2.getValues(),
// get the range of the titles
titleSection1 = outputSheet.getRange(1,1,1, lastCol1),
titleSection2 = sourceSheet.getRange(1,1,1, lastCol2),
// get the values from the titles
titles1 = titleSection1.getValues(),
titles2 = titleSection2.getValues(),
// get the column # for "ID" and "comment"
idCol1 = titles1[0].indexOf("ID"),
idCol2 = titles2[0].indexOf("ID"),
commentsCol1 = titles1[0].indexOf("comment"),
commentsCol2 = titles2[0].indexOf("comment");
// get the IDs from range1
for (i = 1; i < values1.length; i++) {
compare1 = values1[i][idCol1];
// get the IDs from range2
for (j = 1; j< values2.length; j++){
compare2 = values2[j][idCol2];
// if same ID, change the values array
if (compare1 == compare2) {
values1[i][commentsCol1] = values2[j][commentsCol2];
}
}
}
// set values based on the values array
range1.setValues(values1);
}
Instead, if there is a change made to any cell on sheet 1, it will find the identical cell based on unique ID in the other sheet and sync the change. What change do i need to make to have this work?
For example, if I change what the office is in the row of ID 1 of sheet 1, it will make the identical change for ID 1 in sheet 2.
Here is an example sheet of what im working with:
Sheet 1:
ID Comment Number Office Clinician
1 good 22345 Dallas
2 bad 12345 Denton
3 good 95954 Lubbock
4 bad 20204 FT.W
5 bad 11111 Denton
6 good 02944 Preston
Sheet 2:
ID Comment Number Office Clinician
1 good 22345 Dallas
3 good 95954 Lubbock
5 bad 11111 Denton

You have one data set on Sheet 1, and a subset of that data set on Sheet1. The key field is the "ID". If there is a change to data on Sheet 1, and the ID relating to that change is found on Sheet 2, then you want to update the relevant dataset on Sheet 2.
The key aspects of this answer are:
onEdit(e): This is an simple Trigger.
e.range: "range" is an Event Object. By using the attribute 'e'; it is possible to recover a substantial amount of information about the changes. In addition, the event objects can be further used to obtain more information - such as (in this case) the Row, Column and Sheet Name.
filter(String).length: Sometimes getting the last row of data is problematic. The answer gets all the data in Column A, and uses the Javascript "array.filter" method. In this case is simply counts the values as strings, andf the resulting value is equal to the Last Row of data.
the "IF" statement evaluates for several attributes:
Is the edit on Sheet1?
Is the edit on a row between the header and last row?
Is the edit in a column that contains data?
the operator is "&&" which requires that each of the attributes must return true.
targetdata.map(function(e){return e[0];});: the script gets ALL the data on Sheet2, but uses the Javascript array.map method to generate a subject of just the values in Column 1 (the ID column).
targetCol1.indexOf(sourceID);: The script uses the Javascript "array.indexOf"; if the ID is found, it will return the index number of the row on Sheet2; if the value isn't found, it will return "-1". The enables a logic statement to be written that will be executed only if the value is not "-1"
target.getRange(+result+1,1,1,sourceLC).setValues(sourcedatatocopy): the last lines of the script get the values in the edited row on Sheet 1, and then update the values on Sheet2. Note: it updates all the values for the matched ID - rather than identifying the changed field and updating only that field.
function onEdit(e) {
// setup spreadsheet and sheet names
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSN = "Sheet1";
var source = ss.getSheetByName(sourceSN);
var targetSN = "Sheet2";
var target = ss.getSheetByName(targetSN);
// get row, column and sheet of the edited sheet
var row = e.range.getRow();
var col = e.range.getColumn();
var editedsheet = e.range.getSheet().getSheetName();
// Logger.log("DEBUG: editedsheet = "+editedsheet)
// get the ID column from Sheet 1
var SourceCol1Vals = ss.getRange("A2:A").getValues();
var SourceLR = SourceCol1Vals.filter(String).length;
// assign value to last column
var sourceLC = 5;
// test for sheet, row range and column range
if (editedsheet == sourceSN && row >=2 && row <=SourceLR && col <=5){
//Logger.log("DEBUG: this is sheet 1 & the right row and the right Col")
// get the data for this row
var sourcedata = source.getRange(row,1,1,5).getValues();
// get the data from target
var targetdata = target.getDataRange().getValues();
// get only the ID column
var targetCol1 = targetdata.map(function(e){return e[0];});
// get the sourse ID
var sourceID = sourcedata[0][0];
// Logger.log("DEBUG: source ID = "+sourceID)
// search for source ID on the Target list
var result = targetCol1.indexOf(sourceID);
// Logger.log("DEBUG: id was found at "+result);
if (result !=-1){
// if -1 then couldn't find the ID, otherwise it returns the index number where it finds the match
// get the data for the Source ID
var sourcedatatocopy = source.getRange(row,1,1,sourceLC).getValues();
// update the darget for the sourceID data.
target.getRange(+result+1,1,1,sourceLC).setValues(sourcedatatocopy)
}
} else{
// Logger.log("DEBUG: not sheet 1 or right row or right col");
}
return;
}

Related

How to copy values from one sheet and paste them into another using Google Sheets Macros?

I'm writing a Google Sheets Macros without having a lot of knowledge about syntax.
What I want to do is the following:
I want to copy the values which are matching in a source matrix into another table. However, I don't know how to write that as a Macros.
I've written the following code:
function CalcularCruces() {
var spreadsheet = SpreadsheetApp.getActive();
var sourceSheet = spreadsheet.getSheetByName("Cruces Activo-Amenazas");
var destinationSheet = spreadsheet.getSheetByName("AnĂ¡lisis de Riesgos");
/** Total number of left column values from source table **/
const maxAmenazas = 29;
for(var i = 0; i < maxAmenazas; i++) {
/** Now I need to get the column and row values which are matching with the checkbox
and paste them into another table **/
}
};
Here is an example of the input table and how the output table should look like after executing the macros.
Input Table Sheet
Output Table Sheet
Edit:
I need the data to be written next to this static columns:
Actual Output
Desired Output
You can do the following:
Retrieve the data from the source sheet via getDataRange and getValues.
For each row in this data (excluding the headers row, that has been retrieved and removed from the array with shift), check which columns have the checkbox marked.
If the corresponding checkbox is marked, write the corresponding values to the destination sheet with setValues.
It could be something like this:
function CalcularCruces() {
var spreadsheet = SpreadsheetApp.getActive();
var sourceSheet = spreadsheet.getSheetByName("Cruces Activo-Amenazas");
var destinationSheet = spreadsheet.getSheetByName("AnĂ¡lisis de Riesgos");
destinationSheet.getRange("A2:B").clearContent();
var values = sourceSheet.getDataRange().getValues(); // 2D array with all data from source sheet
var headers = values.shift(); // Remove and retrieve the headers row
for (var i = 1; i < values[0].length; i++) { // Iterate through each column
for (var j = 0; j < values.length; j++) { // Iterate through each row
var activo = values[j][0]; // Activo corresponding to this row
if (values[j][i]) { // Check that checkbox is marked
// Get the row index to write to (first row in which column A and B are empty):
var firstRow = 2;
var firstCol = 1;
var numRows = destinationSheet.getLastRow() - firstRow + 1;
var numCols = 2;
var firstEmptyRow = destinationSheet.getRange(firstRow, firstCol, numRows, numCols).getValues().filter(function(row) {
return row[0] !== "" && row[1] !== "";
}).length + firstRow;
// Write data to first row with empty columns A/B:
destinationSheet.getRange(firstEmptyRow, firstCol, 1, numCols).setValues([[headers[i], activo]]);
}
}
}
};
Notes:
All data is added to the target sheet every time the script is run, and this can lead to duplicate rows. If you want to avoid that, you can use clearContent at the beginning of your script, after declaring destinationSheet, to remove all previous content (headers excluded):
destinationSheet.getRange("A2:B").clearContent();
In this sample, the number of amenazas is not hard-coded, but it dynamically gets the number of rows in the source sheet with getValues().length. I'm assuming that's a good outcome for you.
UPDATE: Since you have other columns in your target sheet, you cannot use appendRow but setValues. First, you have to find the index of the first row in which columns A and B are empty. This is achieved with filtering the array of values in columns A-B and filtering out the elements in which the two values are empty (with filter).
Reference:
Sheet.getDataRange
Range.getValues
Array.prototype.shift()
Sheet.appendRow(rowContents)
Array.prototype.filter()
Range.clearContent()

How do I extract a specific Column from google sheets using Google Script?

So I have an meal inventory spreadsheet that is fed by a google form. Employees count meals at the end of the day every night and each 2nd cell of each column has a title: Initial, Monday, Tuesday you get the idea. I'd like to notify myself when certain items are sold at a fast rate so that they can be replenished. My problem is that I can easily get data from the most recent night using
.getLastColumn();
but I want to base each lastColumn data from the most recent column that has "Initial" in the second cell of the column and put them in a 2 column array on a seperate sheet. any ideas? this is my script so far;
function activeData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var Sheet = ss.getSheetByName("IREmaildata");
var lastCol = Sheet.getLastColumn();
var rangeInitial = Sheet.getDataRange(); // Not sure if this would be correct?
var initial = rangeInitial.getValues();
var int = "Initial"; //the 3rd cell in "Initial" column must say "Initial".
var rangeLastnight = Sheet.getRange(1,lastCol,37,1); //I can easily get the data from the most recent googleForm submission.
var lastNight = rangeLastnight.getValues(); //I can easily get the data from the most recent googleForm submission.
/*
probably some type of logic will go here?
*/
ss.getSheetByName("Sheet5").getRange(1,2,37,1).setValues(yesterday);
ss.getSheetByName("Sheet5").getRange(1,3,37,1).setValues(today);
}
My Spreadsheet; https://docs.google.com/spreadsheets/d/1X_UcqyXXRMyjZ2j46TymMrIMWvt19HOZTTaEUlIVqwE/edit?usp=sharing
In your shared Spreadsheet, the sheet of IREmaildata and Sheet5 are the input and output you expect, respectively.
From your shared Spreadsheet, you want to retrieve the values from the sheet IREmaildata.
You want to retrieve the last column (Most recent Night Dynamic Column), and retrieve the column (Begining of the week Dynamic Column) which has Initial at the 2nd row and the closest from the last column. And the column "A".
And you want to put the values, which were calculated, to new column (Precenatage sold Static Column).
For your shared Spreadsheet, you want to retrieve the column "A", "V", "Y" and the calculated values to the other sheet in the Spreadsheet.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Flow:
The flow of this sample script is as follows.
Retrieve the values from the data range of the sheet IREmaildata.
Retrieve the index of the last column from the values.
Retrieve the index of the column which has the value of Initial at the row 2.
Retrieve the column "A", the index of the column which has the value of Initial at the row 2, the index of the last column.
Calculate the percentage and put the value to the array for putting to Spreadsheet.
Put the result values to the Spreadsheet.
Sample script:
Please copy and paste the following script to the container-bound script of the shared Spreadsheet. And please prepare the destination sheet and set the destination sheet name to destSheetName, and run the function activeData.
function activeData() {
var destSheetName = "###"; // Please set the destination sheet name.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("IREmaildata");
var values = sheet.getDataRange().getValues();
var lastColumnIndex = values[1].length - 1;
var initialIndex = 0;
for (var i = lastColumnIndex - 1; i >= 0; i--) {
if (values[1][i] == "Initial") {
initialIndex = i;
break;
}
}
var result = values.map(function(row, i) {
var ar = [row[0], row[initialIndex], row[lastColumnIndex]];
if (i == 0) {
ar.push("");
} else if (i == 1) {
ar.push("Since Begining of week");
} else if (i > 1) {
var temp = 1 - (row[lastColumnIndex] / row[initialIndex]);
ar.push(isNaN(temp) ? "None sold" : temp);
}
return ar;
});
var destSheet = ss.getSheetByName(destSheetName);
destSheet.getRange("D3:D").setNumberFormat("0.00%");
destSheet.getRange(1, 1, result.length, result[0].length).setValues(result);
}
Note:
At above script, getValues() is used for retrieving the values from the sheet IREmaildata. In this case, the date values are retrieved as the date object. If you want to fix the date format, for example, please modify var values = sheet.getDataRange().getValues(); to var values = sheet.getDataRange().getDisplayValues();.
References:
map()
setNumberFormat()

Is it possible to pull different data sets from one column?

I've been trying to write some code that looks down one column with strings based on some simple formulas. I can't seem to get it to recognize the different sets of data and paste them where I want them.
I have tried re writing the code a few different ways in which is looks at all the data and just offsets the destination row by 1. But it does not recognize that it is pull different data.
Below is the code that works. What it does is starts from the 1st column 2nd row (where my data starts). The data is a list like;
A
1 Customer1
2 item1
3 item2
4 Item3
5
6 Customer2
7 Item1
The formulas that I have in those cells just concatenates some other cells.
Using a loop it looks through column A and find the blank space. It then "breaks" whatever number it stops on, the numerical A1 notation of the cell, it then finds the values for those cells and transposes them In another sheet in the correct row.
The issue I am having with the code this code that has worked the best is it doesn't read any of the cells as blank
(because of the formulas?) and it transposes all to the same row.
function transpose(){
var data = SpreadsheetApp.getActiveSpreadsheet();
var input =data.getSheetByName("EMAIL INPUT");
var output = data.getSheetByName("EMAIL OUTPUT");
var lr =input.getLastRow();
for (var i=2;i<20;i++){
var cell = input.getRange(i, 1).getValue();
if (cell == ""){
break
}
}
var set = input.getRange(2, 1, i-1).getValues();
output.getRange(2,1,set[0].length,set.length) .
.setValues(Object.keys(set[0]).map ( function (columnNumber) {
return set.map( function (row) {
return row[columnNumber];
});
}));
Logger.log(i);
Logger.log(set);
}
What I need the code to do is look through all the data and separate the sets of data by a condition.
Then Transpose that information on another sheet. Each set (or array) of data will go into a different row. With each component filling across the column (["customer1", "Item1","Item2"].
EDIT:
Is it Possible to pull different data sets from a single column and turn them into arrays? I believe being able to do that will work if I use "appendrow" to tranpose my different arrays to where I need them.
Test for the length of cell. Even if it is a formula, it will evaluate the result based on the value.
if (cell.length !=0){
// the cell is NOT empty, so do this
}
else
{
// the cell IS empty, so do this instead
}
EXTRA
This code takes your objective and completes the transposition of data.
The code is not as efficient as it might/should because it includes getRange and setValues inside the loop.
Ideally the entire Output Range could/should be set in one command, but the (unanswered) challenge to this is knowing in advance the maximum number rows per contiguous range so that blank values can be set for rows that have less than the maximum number of rows.
This would be a worthwhile change to make.
function so5671809203() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var inputsheetname = "EMAIL_INPUT";
var inputsheet = ss.getSheetByName(inputsheetname);
var outputsheetname = "EMAIL_OUTPUT";
var outputsheet = ss.getSheetByName(outputsheetname);
var inputLR =inputsheet.getLastRow();
Logger.log("DEBUG: the last row = "+inputLR);
var inputrange = inputsheet.getRange(1, 1,inputLR+1);
Logger.log("the input range = "+inputrange.getA1Notation());
var values = inputrange.getValues();
var outputval=[];
var outputrow=[];
var counter = 0; // to count number of columns in array
for (i=0;i<inputLR+1;i++){
Logger.log("DEBUG: Row:"+i+", Value = "+values [i][0]+", Length = "+values [i][0].length);
if (values [i][0].length !=0){
// add this to the output sheet
outputrow.push(values [i][0]);
counter = counter+1;
Logger.log("DEBUG: value = "+values [i][0]+" to be added to array. New Array Value = "+outputrow+", counter = "+counter);
}
else
{
// do nothing with the cell, but add the existing values to the output sheet
Logger.log("DEBUG: Found a space - time to update output");
// push the values onto an clean array
outputval.push(outputrow);
// reset the row array
outputrow = [];
// get the last row of the output sheet
var outputLR =outputsheet.getLastRow();
Logger.log("DEBUG: output last row = "+outputLR);
// defie the output range
var outputrange = outputsheet.getRange((+outputLR+1),1,1,counter);
Logger.log("DEBUG: the output range = "+outputrange.getA1Notation());
// update the values with array
outputrange.setValues(outputval);
// reset the row counter
counter = 0;
//reset the output value array
outputval=[];
}
}
}
Email Input and Output Sheets

Compare 4 different columns in 2 different Google Sheets

I am trying to compare the data from 2 google sheets. Each sheet has a column that is the identifier (sheet1:H and sheet2:C), if these match then I want to change sheet1:I to the value in sheet2:E. I'm running this code, but get no errors. It's not working though.
I tried to see similar posts this issue but they all seem to be lacking the compare a different column method I am using.
function changestatus() {
// gets spreadsheet A and the range of data
ssA = SpreadsheetApp.openById('IDHERE');
sheetA = ssA.getSheetByName('Sheet1');
dataA = sheetA.getRange('H2:H').getValues();
dataD = sheetA.getRange('I2:I').getValues();
// gets spreadsheet B and the range of data
ssB = SpreadsheetApp.openById('IDHERE');
sheetB = ssB.getSheetByName('responses');
dataB = sheetB.getRange('C2:C').getValues();
dataC = sheetB.getRange('E2:E').getValues();
for (var i = 0; i > sheetA.getLastRow(); i++) {
if (dataA[1][i] == dataB[1][i] && dataC[1][i] != dataD[1][i]){
var value = sheetA.getRange(i+1, 2).getValue(dataD);
sheetB.getRange(i+1, 2).setValue(value);
} // end if
} // end i
Starting results of sheets files would be something like:
Sheet 1
H:(ID) 1 I:(grade) pass
Sheet 2
C:(ID) 1 E:(grade) fail
After Function:
Sheet 1
H:(ID) 1 I:(grade) fail
#tehhowch is quite right; you need to review JavaScript comparison operators, for loop syntax, the format of object returned by Range#getValues, and how to access JavaScript array indices. Each of these contributes to your code problems, but it's reasonable that that we help you along the road a little more.
Loop syntax
This is an easy one. Instead of "i > sheetA.getLastRow()", it should read i < sheetA.getLastRow(). i starts with a value of zero, and its value increases by one at the end of each loop; so you want the loop to process all the values of i that are less than the value of the last row.
Array values
getValues returns a two-dimensional array but the IF statement fails because the array values are back to front.
For example, instead of "dataA[1][i]", it should be dataA[i][0]. There are two changes here:
1 - "i" moves to the first half of the array value (the 'row' value); and
2 - the second half of the array value is [0] (not "[1]"). This is because each variable is only one column wide. For example, dataA only returns the value of column H; same is true for dataB, dataC and dataD - they all return the value of just one column.
Troubleshooting
How could you tell whether the IF statement was a problem? It "looks" OK. One way is to display (or log) the values being returned.
I use Logger.log() (there are other options) to display information in the script editor under "View, Logs". Each time the script is run, the "Logger" statements are updated and you can check their value.
For example, you could insert this code at line 13 (before the loop) to check some values of the data variables.
Logger.log("dataA[1][0] = "+dataA[1][0]);
That line will show: "dataA[1][0] = 2". That's a valid result but you might notice that it is reporting ID=2 but, say, you were expecting a result of ID=1.
So change the line to:
Logger.log("dataA[1][1] = "+dataA[1][1]);
This line shows "dataA[1][1] = undefined". OK, something definitely wrong.
So, let's try:
Logger.log("dataA[0][0] = "+dataA[0][0]);
This line shows "dataA[0][0] = 1". Now that's more like it.
You can make Logger long or short; for example, you might want to evaluate the results of of the variables in one line. So the Logger might look like this:
Logger.log("dataA[0][0] = "+dataA[0][0]+", dataB[0][0] = "+dataB[0][0]+", dataC[0][0] = "+dataC[0][0]+", dataD[0][0] = "+dataD[0][0]);
And it would return:
"dataA[0][0] = 1, dataB[0][0] = 1, dataC[0][0] = Fail, dataD[0][0] = Pass".
This might confirm that you are on the right track, or that you need to debug further
The Failing IF statement
Original line = "(dataA[1][i] == dataB[1][i] && dataC[1][i] != dataD[1][i])"
Corrected line = (dataA[i][0] == dataB[i][0] && dataC[i][0] != dataD[i][0])
Updating the results on Sheet 1
The code here is:
var value = sheetA.getRange(i+1, 2).getValue(dataD);
sheetB.getRange(i+1, 2).setValue(value);
This is confusing and complicates a couple of things.
1 - the value just needs to be "the value in sheet2:E - this was in the IF statement: dataC[i][0]. So value = dataC[i][0]
2 - The goal is "change sheet1:I to the value in sheet2:E". You've already got the value, so focus now on sheet1:I.
Some times it is more simple to define the range and then, on a second line, update the value for that range.
the target sheet is sheetA;
the target row is: i+1 (that was correct);
the target column is: I (or column 9).
So, var range = sheetA.getRange(i+2, 9);
You could check this with "Logger":
Logger.log("range = "+range.getA1Notation()); might return "range = I2".
Then update the value:
range.setValue(value);
Meaningful variable names
It helps (a LOT) to use meaningful variable names. For example, the original code uses:
"dataA" = Sheet1, Column H (contains ID); so maybe this could be "data1_H" or even "targetID.
"dataD" = Sheet1, Column I (contains grade); so maybe this could be "data1_I" or targetGrade.
"dataB" = Sheet2, Column C (contains ID), so maybe this could be "data2_C" or sourceID.
"dataC" = Sheet2, Column E (contains grade); so maybe this could be "data2_E" or sourceGrade.
Summary of changes
function so_changestatus() {
// gets spreadsheet A and the range of data
ssA = SpreadsheetApp.openById('IDHERE');
sheetA = ssA.getSheetByName('Sheet1');
dataA = sheetA.getRange('H2:H').getValues();
dataD = sheetA.getRange('I2:I').getValues();
// gets spreadsheet B and the range of data
ssB = SpreadsheetApp.openById('IDHERE');
sheetB = ssB.getSheetByName('responses');
dataB = sheetB.getRange('C2:C').getValues();
dataC = sheetB.getRange('E2:E').getValues();
for (var i = 0; i < sheetA.getLastRow(); i++) {
if (dataA[i][0] == dataB[i][0] && dataC[i][0] != dataD[i][0]){
var value = dataC[i][0];
var range = sheetA.getRange(i+2, 9);
range.setValue(value);
} // end if
}
}
UPDATE - 1 April 2019
ID on SheetA vs SheetB does NOT match row-by-row
The original code was written on the basis that the ID matched on a row-by-row basis. This is not the case. So a variation in the code is needed to test whether the ID on SheetA exists on SheetB, and then test the respective status.
The evaluation of the sheetA ID on sheetB is done with [indexof] Docs reference.
In this code, I also took the opportunity to make the variable names of the data ranges more meaningful.
Note also: the loop continues while i is less than the lastrow minus one "i < (lastrow-1);". This is necessary because the first row are headers and the data range starts on row 2, so the number of data rows will be the "lastrow minus one" (to a allow for the header row).
function ejb2so_changestatus() {
// gets spreadsheet A and the range of data
// ssA = SpreadsheetApp.openById('IDHERE');
ssA = SpreadsheetApp.getActive();
sheetA = ssA.getSheetByName('Sheet1');
dataA_ID = sheetA.getRange('H2:H').getValues();
data_Status = sheetA.getRange('I2:I').getValues();
//Logger.log("DEBUG: H3 = "+dataA_ID[4][0]+", I3 = "+data_Status[4][0]);//DEBUG
// gets spreadsheet B and the range of data
//ssB = SpreadsheetApp.openById('IDHERE');
ssB = SpreadsheetApp.getActive();
sheetB = ssB.getSheetByName('Responses');
dataB_ID = sheetB.getRange('C2:C').getValues();
dataB_Status = sheetB.getRange('E2:E').getValues();
// Logger.log("DEBUG: C3 = "+dataB_ID[0][0]+", E3 = "+dataB_Status[0][0]);//DEBUG
var lastrow = sheetA.getLastRow()
// Logger.log("DEBUG: sheetA last row = "+lastrow);//DEBUG
// Flatten the array
var dataB_IDFlat = dataB_ID.map(function(row) {
return row[0];
});
//Loop through values on sheetA; check if they exist on sheetB
for (var i = 0; i < (lastrow - 1); i++) {
var A_ID = dataA_ID[i][0];
// Logger.log("DEBUG: id = "+A_ID);//DEBUG
// assign variable to return value index
var result = dataB_IDFlat.indexOf(A_ID);
if (result != -1) {
// it's there
// Logger.log("DEBUG: i: "+i+", ID: "+A_ID+", it's there"+", result#: "+result);//DEBUG
// Logger.log("DEBUG: Sheet1 status: "+data_Status[i][0]+" Vs Sheet2 status = "+dataB_Status[result][0]);//DEBUG
// compare status from sheetsA to sheetB
if (data_Status[i][0] != dataB_Status[result][0]) {
// Logger.log("DEBUG: status change to: "+dataB_Status[result][0]);//DEBUG
var range = sheetA.getRange(i + 2, 9);
//Logger.log("DEBUG: value = "+value);//DEBUG
//Logger.log("DEBUG: range = "+range.getA1Notation());//DEBUG
range.setValue(dataB_Status[result][0]);
}
} else {
// it's not there
// Logger.log("DEBUG: i: "+i+", ID: "+A_ID+", it's not there");//DEBUG
}
}
}
// Credit: Flatten array: https://stackoverflow.com/a/49354635/1330560

Paste values from one Google Sheet to another and remove duplicates based on ID column

I have a similar situation to the one described on this question: two worksheets, with input data coming into the Feed sheet using the importxml function and a Data sheet where new rows get copied thanks to a script set to run daily.
However, the current script is creating daily duplicates. As such, I would like to adapt the answer provided on the question above so that the script checks the IDs on column F and only copies the rows with new IDs.
How should I update the section below that creates a hash to one that looks for the IDs on column F instead? Also my rows are consistent, so is it correct to assume I can just remove the relevant code lines towards the end?
The sample Google Sheet is available here.
function appendUniqueRows() {
var ss = SpreadsheetApp.getActive();
var sourceSheet = ss.getSheetByName('Get Data');
var destSheet = ss.getSheetByName('Final Data');
var sourceData = sourceSheet.getDataRange().getValues();
var destData = destSheet.getDataRange().getValues();
// Check whether destination sheet is empty
if (destData.length === 1 && "" === destData[0].join('')) {
// Empty, so ignore the phantom row
destData = [];
}
// Generate hash for comparisons
var destHash = {};
destData.forEach(function(row) {
destHash[row.join('')] = true; // could be anything
});
// Concatentate source rows to dest rows if they satisfy a uniqueness filter
var mergedData = destData.concat(sourceData.filter(function (row) {
var hashedRow = row.join('');
if (!destHash.hasOwnProperty(hashedRow)) {
// This row is unique
destHash[hashedRow] = true; // Add to hash for future comparisons
return true; // filter -> true
}
return false; // not unique, filter -> false
}));
// Check whether two data sets were the same width
var sourceWidth = (sourceData.length > 0) ? sourceData[0].length : 0;
var destWidth = (destData.length > 0) ? destData[0].length : 0;
if (sourceWidth !== destWidth) {
// Pad out all columns for the new row
var mergedWidth = Math.max(sourceWidth,destWidth);
for (var row=0; row<mergedData.length; row++) {
for (var col=mergedData[row].length; col<mergedWidth; col++)
mergedData[row].push('');
}
}
// Write merged data to destination sheet
destSheet.getRange(1, 1, mergedData.length, mergedData[0].length)
.setValues(mergedData);
}
I'm a novice in this world of Google Apps scripts, so do please let me know if I'm missing any crucial information. Thanks in advance for the help.
You want to copy the values from "Feed" sheet to "Data" sheet.
When the values are copied, you want to copy only new values which are not included in "Data" sheet.
You want to choose the new values using the values of column "F".
If my understanding for your question is correct, how about this modification? In this modification, I modified the script in your shared spreadsheet.
Modification points:
In your script, all values of "Feed" sheet are copied to "Data" sheet. So in order to choose only new values, I used the following flow.
Retrieve the values from column "F". This is used for choosing the new values.
Retrieve the new values using the values from column "F".
Put the new values to "Data" sheet.
The script which reflected above flow is as follows.
Modified script:
From:
This is your script in the shared spreadsheet. Please modify this script to below one.
function Copy() {
var sss = SpreadsheetApp.openById('#####'); // this is your Spreadsheet key
var ss = sss.getSheetByName('Feed'); // this is the name of your source Sheet tab
var range = ss.getRange('A3:H52'); //assign the range you want to copy
var data = range.getValues();
var tss = SpreadsheetApp.openById('#####'); //replace with destination ID
var ts = tss.getSheetByName('Data'); //replace with destination Sheet tab name
ts.getRange(ts.getLastRow()+1, 1,50,8).setValues(data);// 49 value refers to number of rows, 8 to columns
}
To:
function Copy() {
var sss = SpreadsheetApp.openById('#####'); // this is your Spreadsheet key
var ss = sss.getSheetByName('Feed'); // this is the name of your source Sheet tab
var range = ss.getRange('A3:H52'); //assign the range you want to copy
var data = range.getValues();
var tss = SpreadsheetApp.openById('#####'); //replace with destination ID
var ts = tss.getSheetByName('Data'); //replace with destination Sheet tab name
// Below script was added.
var values = ts.getRange("F3:F").getValues().filter(String);
var copiedValues = data.filter(function(e) {return !values.some(function(f){return f[0] == e[5]}) && e.filter(String).length > 0});
ts.getRange(ts.getLastRow() + 1, 1, copiedValues.length, copiedValues[0].length).setValues(copiedValues);
}

Categories

Resources