Compare 4 different columns in 2 different Google

2019-09-20 18:05发布

问题:

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

回答1:

@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