Q: How to update an old spreadsheets with a new one, adding only new elements and changing color of subtracted elements? I have a problem similar to this: Imagine I have a Google Sheet (eg, with students info) which I get downloading from a school site (imagine the school system is very bad, so it's more useful to use a sheet with my Google Scripts). But I have to weekly update this sheet downloading a new sheet from school site. To not lose my previous notes, I want to write a script that makes the update process, this way:
 - If the most recent sheet has a new row which isn't in the previous sheet (a new student row), it add this row to the new sheet; 
 - If the old sheet has a row which isn't in the new sheet, it change the color of that row and adds a note "Transferred student" or something like that.
I get this sample code, but I'm wonder if there isn't a better way to do this. This is my code:
function updateSheet(){
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var sourceSpreadsheetID = ss.getId();
    var oldSpreadsheet = SpreadsheetApp.openById(sourceSpreadsheetID);
    var oldWorksheet = oldSpreadsheet.getSheetByName("students");
    var oldData = oldWorksheet.getDataRange().getValues();  
    var newSpreadsheetUrl = Browser.inputBox("New sheet", "Put the new sheet link here:", Browser.Buttons.OK_CANCEL);
    Logger.log("newSpreadsheetUrl = " + newSpreadsheetUrl);
    var newSpreadsheetID = newSpreadsheetUrl.split('d/')[1].split('/')[0];
    var newSpreadsheet = SpreadsheetApp.openById(newSpreadsheetID);
    var newWorksheet = newSpreadsheet.getSheetByName("students");
    var newData = newWorksheet.getDataRange().getValues();
  // Iterates through the new sheet rows
    for(i=1; i<newData.length; i++){
    var alreadyInOldSheet = false;
    var remainsInNewSheet = false;
      Logger.log("newData[i][0] = " + newData[i][0]);
    // Iterates through the old sheet rows
        for(j=1; j<oldData.length; j++){
            // compares the firs cell (student name)
            Logger.log("oldData[j][0] = " + oldData[j][0]);
            Logger.log("newData[i][0] == oldData[j][0] = " + (newData[i][0] == oldData[j][0]) );
            if (newData[i][0] == oldData[j][0]) {                
                alreadyInOldSheet = true;
                break; // This student is already in the old sheet, so, jump to the next row
            }            
        // After iterates through all old sheet rows, the student name isn't found, so we add it
        }
          Logger.log("Last condition 'alreadyInOldSheet =' " + alreadyInOldSheet);
          Logger.log("newData =' " + newData);
          if (alreadyInOldSheet == false) {
             oldWorksheet.appendRow(newData[i]);
         }
  }
  absentInNewSheet(oldData, newData, oldWorksheet);
}


function absentInNewSheet(oldData, newData, workSheet) {
   for(i=1; i<newData.length; i++){
    var alreadyInOldSheet = false;
    Logger.log("newData[i][0] = " + newData[i][0]);
    // Iterates through the old sheet rows
        for(j=1; j<oldData.length; j++){
            // compares the firs cell (student name)
            Logger.log("oldData[j][0] = " + oldData[j][0]);
            Logger.log("newData[i][0] == oldData[j][0] = " + (newData[i][0] == oldData[j][0]) );
            if (newData[i][0] == oldData[j][0]) {                
                alreadyInOldSheet = true;
                break; // This student is already in the old sheet, so, jump to the next row
            }
        // After iterates through all old sheet rows, the student name isn't found, so we add it
        }


A: Example Code
function updateSheet()
{
  var ss = SpreadsheetApp.getActiveSpreadsheet();

  var masterSheet = ss.getSheetByName("Master");
  var masterRange = masterSheet.getDataRange();
  var masterData = masterRange.getValues();
  masterData.shift();
  var masterBackgrounds = masterRange.getBackgrounds();
  var masterNotes = masterRange.getNotes();
  var masterLength = masterNotes.length;

  var updateData = ss.getSheetByName("Update").getDataRange().getValues();
  updateData.shift();

  for (var i = 0; i < (masterLength - 1); i++)
  {
    masterData[i].unshift(i + 1);
  }
  masterData.sort(function(a,b) {return (a[1] > b[1]) ? 1 : ((a[1] < b[1]) ? -1 : 0 );});
  updateData.sort(function(a,b) {return (a[0] > b[0]) ? 1 : ((a[0] < b[0]) ? -1 : 0 );});

  var addedData = [];
  while (masterData.length || updateData.length)
  {
    if (!masterData.length || (updateData.length && masterData[0][1] > updateData[0][0]))
    {
      addedData.push(updateData.splice(0, 1)[0]);
    }
    else if (!updateData.length || (masterData.length && masterData[0][1] < updateData[0][0]))
    {
      for (var k = 0; k < masterBackgrounds[0].length; k++)
      {
        masterBackgrounds[masterData[0][0]][k] = "#dcdcdc";
      }
      masterNotes[masterData[0][0]][0] = "This student was transfered from this school";
      masterData.shift();
    }
    else
    {
      masterData.shift();
      updateData.shift();
    }
  }

  var extraRows = masterLength + addedData.length - masterSheet.getMaxRows();
  if (extraRows > 0) masterSheet.insertRowsAfter(masterLength, extraRows);

  if (addedData.length > 0) masterSheet.getRange(masterLength + 1, 1, addedData.length, addedData[0].length).setValues(addedData);
  masterRange.setBackgrounds(masterBackgrounds).setNotes(masterNotes);  
}

Test spreadsheet (feel free to try out - hopefully you can adjust this to your specific spreadsheets)
Thanks for re-posting; I just happened to be helped with a similar problem I posted when the Google Apps Script forum was over at GPF (I can't find that thread at all, but credit to +ScampMichael for helping me out; I have fine-tuned the script a bit since).
In general, the algorithm is to sort both the master and update arrays, and work through each array simultaneously, comparing the first element of each and performing some action then, including shift()-ing that element off each array when you're done with it. Before sorting the master array, in your case you would need to also append an index column so you know which cells to apply the note/background to - which with this method you do in one batch set for each, which should also improve performance.
Anyway, I'm not suggesting there aren't even more efficient algorithms, but when I had thousands of rows in my situation, it took about 8 seconds, while the "multiple looping" method would often time out. I hope it helps.

A: This is my actual code to perform this task. Maybe it isn't the more efficient solution (any suggestions?) but it is working for this problem. 
function updateSheet(){
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var sourceSpreadsheetID = ss.getId();
    var oldSpreadsheet = SpreadsheetApp.openById(sourceSpreadsheetID);
    var oldWorksheet = oldSpreadsheet.getSheetByName("students");
    var oldData = oldWorksheet.getDataRange().getValues();  
    var newSpreadsheetUrl = Browser.inputBox("New sheet", "Put the new sheet link here:", Browser.Buttons.OK_CANCEL);
  //  Logger.log("newSpreadsheetUrl = " + newSpreadsheetUrl);
    var newSpreadsheetID = newSpreadsheetUrl.split('d/')[1].split('/')[0];
    var newSpreadsheet = SpreadsheetApp.openById(newSpreadsheetID);
    var newWorksheet = newSpreadsheet.getSheetByName("students");
    var newData = newWorksheet.getDataRange().getValues();
  // Iterates through the new sheet rows
    for(i=1; i<newData.length; i++){
    var alreadyInOldSheet = false;
    var remainsInNewSheet = false;
//      Logger.log("newData[i][0] = " + newData[i][0]);
    // Iterates through the old sheet rows
        for(j=1; j<oldData.length; j++){
            // compares the firs cell (student name)
        //    Logger.log("oldData[j][0] = " + oldData[j][0]);
         //   Logger.log("newData[i][0] == oldData[j][0] = " + (newData[i][0] == oldData[j][0]) );
            if (newData[i][0] == oldData[j][0]) {                
                alreadyInOldSheet = true;
                break; // This student is already in the old sheet, so, jump to the next row
            }            
        // After iterates through all old sheet rows, the student name isn't found, so we add it
        }
      //    Logger.log("Last condition 'alreadyInOldSheet =' " + alreadyInOldSheet);
      //    Logger.log("newData =' " + newData);
          if (alreadyInOldSheet == false) {
             oldWorksheet.appendRow(newData[i]);
         }
  }
  absentInNewSheet(oldWorksheet.getDataRange().getValues(), newData, oldWorksheet);
}


function absentInNewSheet(oldData, newData, workSheet) {
   for(i=1; i<oldData.length; i++){
    var remainsInNewSheet = false;
    Logger.log("oldData[i][0] = " + oldData[i][0]);
    // Iterates through the old sheet rows
     Logger.log("oldData.length = " + oldData.length);
        for(j=1; j<newData.length; j++){
            // compares the firs cell (student name)
            Logger.log("newData[j][0] = " + newData[j][0]);
            Logger.log("first condition oldData[i][0] == newData[j][0] = " + (oldData[i][0] == newData[j][0]) );
            if (oldData[i][0] == newData[j][0]) {                
                remainsInNewSheet = true;
                Logger.log("break");
                break; // This student is already in the old sheet, so, jump to the next row
            }
        // After iterates through all old sheet rows, the student name isn't found, so we add it
        }
          Logger.log("Last condition 'remainsInNewSheet =' " + remainsInNewSheet);
          Logger.log(" ");
          //Logger.log("newData =' " + newData);     
          if (remainsInNewSheet == false) {
             var lastColumn = workSheet.getLastColumn();
             var firstColumn = workSheet.getLastColumn(); 
             var currentRow = workSheet.getRange(i+1, 1, 1, lastColumn);
             currentRow.setBackgroundRGB(220, 220, 220).setNote("This student was transfered from this school");             
         }
    }
}

