Skip to content

Instantly share code, notes, and snippets.

@palumbo
Created May 29, 2022 14:17
Show Gist options
  • Save palumbo/e47839bcdb7a484d50a7c6e36c861928 to your computer and use it in GitHub Desktop.
Save palumbo/e47839bcdb7a484d50a7c6e36c861928 to your computer and use it in GitHub Desktop.
Google Apps Script that copies rows to a new sheet based on a cell value.
function onOpen(e) {
let ui = SpreadsheetApp.getUi();
ui.createMenu('🤖 Automation Tools')
.addItem('Move reps to individual sheets', 'moveRows')
.addToUi();
};
function moveRows() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();
const eddieSheet = SpreadsheetApp.openById('1wvrKeJd2zj3_tT1W0jkXoIZEbGf4_-0SSSesRgtK_Og').getSheetByName('Sheet1');
const eddieLastRow = eddieSheet.getLastRow();
const josephSheet = SpreadsheetApp.openById('1U0xTv-0_dyFsF3AwwDzT86cjqySjnJcZkn4d4n0MZ1k').getSheetByName('Sheet1');
const josephLastRow = josephSheet.getLastRow();
let lastRow = sheet.getLastRow();
let sortRange = sheet.getSheetValues(2,1,lastRow, 4);
Logger.log(lastRow)
// Logger.log(typeof(sortRange))
// Logger.log(sortRange.length);
let josephCounter = 1;
let eddieCounter = 1;
for (var i = 1; i <= sortRange.length; i++) {
let name = sheet.getRange(i,1).getValue();
console.log(i + " - " + name);
if (name == "Joseph Palumbo") {
let rowValues = sheet.getRange(i, 1, 1, 4).getValues();
josephSheet.getRange(josephLastRow+josephCounter, 1, 1, 4).setValues(rowValues);
josephCounter++;
}
if (name == "Eddie Jauregui") {
let rowValues = sheet.getRange(i, 1, 1, 4).getValues();
eddieSheet.getRange(eddieLastRow+eddieCounter, 1, 1, 4).setValues(rowValues);
eddieCounter++;
}
};
};
@KamenRider1989
Copy link

@palumbo Thanks so much for the assistance. I tried changing the 3rd value to just 1 but I have this error:
"Exception: The number of rows in the data does not match the number of rows in the range. The data has 1 but the range has 12704.
moveRows @ moveRows.gs:24"

@KamenRider1989
Copy link

@palumbo I tried minimizing the number of columns and rows to transfer and it actually worked. Also, with 12k rows and 40 columns, the script reaches its maximum run time. When it hits around 7K rows already with just 4 columns, it exceeds the maximum time. So I guess there's really no problem with your formula, it's on the size of the raw data that's giving the script a hard time. :)

@palumbo
Copy link
Author

palumbo commented Dec 14, 2023

@KamenRider1989 That's definitely a problem with Apps Script since it has to run through Google's infrastructure. The way I've done this in the past is to output the rows being evaluated into Logger.log so I can see the last row worked when the script finishes.

So, for example, the last row worked might be 1200

Before starting the next run I adjust the data range to start with row 1201 to avoid any duplication of work.

@KamenRider1989
Copy link

@palumbo What I'm thinking is to use 2 functions. 1 for the first have and 1 for 2nd half. Do you think that's possible? Like on the first half, instead of using lastRow, i'll specify the end range. then on the 2nd function, i'll specify the start row to the next row range of the ending range of the first half.

like for the first half,
let sortRange = sheet.getSheetValues(15,1,6000, 40);

then on the 2nd half, instead of starting at row 15, i'll have this:
let sortRange = sheet.getSheetValues(6001,1,lastRow, 40);

@palumbo
Copy link
Author

palumbo commented Dec 14, 2023

@KamenRider1989 I think that's a good idea. I'd put them in a single function so they run consecutively like this:

function myFunction(){

firstHalfFunction(); 
secondHalfFunction(); 

}

@KamenRider1989
Copy link

@palumbo Thanks for the input. I'd do this one. Thank you for all the assistance! :)

@daulatliberty
Copy link

Hi @palumbo Thank you for the script and your video on Youtube very helpful. In my case in order to get the last data based on Yesterday (Day -1). Could you help me how to set a criteria that only copy based on certain date and paste to the last row in destination sheet?

@palumbo
Copy link
Author

palumbo commented Jan 16, 2024

@daulatliberty To do this, I would first get the date using the following code:

var date = new Date();

// add a day
date.setDate(date.getDate() - 1);

After that, I would change line 32 of my code to something like

if (date == date_from_spreadsheet) {
let rowValues = sheet.getRange(i, 1, 1, 4).getValues();
josephSheet.getRange(josephLastRow+josephCounter, 1, 1, 4).setValues(rowValues); 
josephCounter++; 
}

See if you can get that to work.

@CT-34589
Copy link

CT-34589 commented Feb 21, 2024

@palumbo
is there a way to if a row is highlighted in a set colour to copy the row into a new sheen and put it in the next available row
i.e if i highlighted rows 2, 6, 9, 27 it would copy them into the other sheet in rows 2,3,4,5 and if i highlight another that would be coppied into row 5

this is what i got so far

function onEdit(e) {
  try {
    if (!e || !e.range) {
      console.log("No range provided in the event object.");
      return;
    }

    var range = e.range;
    var sheet = range.getSheet();
    
    // Check if the edited range is in the correct sheet
    if (sheet.getName() !== "Your Sheet Name") return;

    var backgroundColor = range.getBackground();
    var targetSheetName = "RAS Vanguard Selected Applicants";
    var greenColor = "#49ad45"; // Update with your desired green color

    // Check if the edited range has the green background color
    if (backgroundColor !== greenColor) return;

    // Copy the row to the target sheet
    var targetSheet = e.source.getSheetByName(targetSheetName);
    if (!targetSheet) {
      targetSheet = e.source.insertSheet(targetSheetName);
    }

    // Clear existing contents in target sheet
    targetSheet.clear();

    // Copy the row to target sheet
    range.copyTo(targetSheet.getRange(targetSheet.getLastRow() + 1, 1), {contentsOnly:true});

    // Optionally: remove the green background color after copying
    range.setBackground(null);
  } catch (error) {
    console.error("An error occurred:", error);
  }
}

@palumbo
Copy link
Author

palumbo commented Feb 21, 2024

Hi @CT-34589, yes, you can absolutely copy a row to another sheet based on background color of that row or cell. I looked over your code and it looks more or less correct to get the desired result. Are you getting errors or is something else going wrong?

@palumbo
Copy link
Author

palumbo commented Feb 21, 2024

Here is a quick and short scrip that copies the value of a cell to another sheet if the cell has a background color of red.

The source sheet is simply called Sheet1 and the target sheet is called Target

function myFunction() {
  let ss = SpreadsheetApp.getActiveSpreadsheet(); 
  let sheet = ss.getActiveSheet(); 
  let cell = sheet.getRange('A1'); 
  let color = cell.getBackground(); 
  Logger.log(color); 

  if (color == '#ff0000') {
    let value = cell.getValue(); 
    ss.getSheetByName('Target').getRange('A1').setValue(value); 
  }
}

@CT-34589
Copy link

Hi @CT-34589, yes, you can absolutely copy a row to another sheet based on background color of that row or cell. I looked over your code and it looks more or less correct to get the desired result. Are you getting errors or is something else going wrong?

It just wasn’t triggering on onEdit not sure why

@palumbo
Copy link
Author

palumbo commented Feb 21, 2024

@CT-34589 just rewrote your script (hastily) to see if I could get it to work, and this does copy the the selected range (a single cell) to the target sheet based on if the background color is red.

  try {
    if (!e || !e.range) {
      console.log("No range provided in the event object.");
      return;
    }

    // setting variables 
    let col = e.range.getColumn(); 
    let row = e.range.getRow(); 
    let ss = SpreadsheetApp.getActiveSpreadsheet(); 
    let sheet = ss.getActiveSheet(); 
    let cell = ss.getActiveSheet().getActiveCell().getA1Notation(); 
    let sheetName = SpreadsheetApp.getActiveSpreadsheet().getSheetName();  
    let backgroundColor = sheet.getRange(cell).getBackground(); 
    
    // Check if the edited range is in the correct sheet
    if (sheetName !== "Your Sheet Name") return;

    // var backgroundColor = ;
    var targetSheetName = "RAS Vanguard Selected Applicants";
    var greenColor = "#ff0000"; // Update with your desired RED color

    // Check if the edited range has the green background color
    if (backgroundColor !== greenColor) {
      Logger.log("Returned on line 33");  // this is a test for me 
      return;
    } 

    // Copy the row to the target sheet
    var targetSheet = ss.getSheetByName(targetSheetName); 
    if (!targetSheet) {
      targetSheet = ss.insertSheet(targetSheetName);
    }

    // Clear existing contents in target sheet
    targetSheet.clear();

    // Copy the row to target sheet
    let rangeToCopy = sheet.getRange(cell);  
    Browser.msgBox(rangeToCopy.getValue()); 
    rangeToCopy.copyTo(targetSheet.getRange(1,1)); 

    // range.copyTo(targetSheet.getRange(targetSheet.getLastRow() + 1, 1), {contentsOnly:true});

    // Optionally: remove the green background color after copying
    // range.setBackground(null);
  } catch (error) {
    console.error("An error occurred:", error);
  }
}

@palumbo
Copy link
Author

palumbo commented Feb 21, 2024

@CT-34589 here's a cleaned up and simplified version of the scrip that does the same thing

function onEdit(e) {

  // setting variables 
  let col = e.range.getColumn(); 
  let row = e.range.getRow(); 
  let ss = SpreadsheetApp.getActiveSpreadsheet(); 
  let sheet = ss.getActiveSheet(); 
  let target = ss.getSheetByName('Target'); 
  let targetLastRow = target.getLastRow(); 
  let cell = ss.getActiveCell().getA1Notation(); 
  let activeCell = sheet.getRange(cell); 
  let orange = "#ff9900"; 
  // Browser.msgBox('It works'); 

  // get background color
  let color = activeCell.getBackground(); 
  
  if (color == "#ff9900") {
    activeCell.copyTo(target.getRange(targetLastRow+1,1)); 
  }
}

@CT-34589
Copy link

@palumbo
i think it may be to do with google scripts and setup as the onEdit trigger isn't causing the script to run, although im not sure why

image

@palumbo
Copy link
Author

palumbo commented Feb 22, 2024

@CT-34589 that looks correct to me. I usually add a this line to my onEdit() functions

Browser.msgBox('It works');

to ensure the onEdit is working. Have you done something similar to ensure it's working?

@daulatliberty
Copy link

@palumbo Thank for this https://gist.github.com/palumbo/e47839bcdb7a484d50a7c6e36c861928?permalink_comment_id=4833915#gistcomment-4833915

I tried and it can run. sorry for my lack of knowledge. Can i ask more, how to using 2 criteria in case want to copy data based on certain date and specific name like current?

@palumbo
Copy link
Author

palumbo commented Feb 28, 2024

Hi @daulatliberty, yes, you can do that and it's relatively easy. All you have to do is to add multiple criteria to your if statements on line 32 and 38.

So, for example, I would write the statement like this: if (name == "Joseph Palumbo" && date=="2/28/2024") using the && to designate that I want the script to check for more than one criteria. The rest of the statement will be exactly the same.

@boudiccamorgana
Copy link

this might not be a thing - I'm super new to trying to code so if it's not possible no worries, but when you define "sortRange" is there a way to notate it so I can skip one column? so for example, transfer columns a-g and i-k but not h?

@palumbo
Copy link
Author

palumbo commented May 16, 2024

@boudiccamorgana I don't think you can "grab" two disparate parts of the spreadsheet using a single 'getRange', but you can create two variables for the different parts of the sheet you want to sort or move.

@boudiccamorgana
Copy link

Sorry I am extremely new to this so I just want to clarify: like to run the script twice with two ranges, or to somehow say get(rangex and rangey) so to speak ? Idk if this helps or makes it worse but the new sheets don’t skip that column so the master sheet’s “I” would be the new one’s “h” and move it in a row.

@palumbo
Copy link
Author

palumbo commented May 16, 2024

@boudiccamorgana Think of the ranges like lego blocks. You can cut up the first sheet any way you want by defining different ranges that you want to copy, then you can put them wherever you want on the target sheet, combining the disparate ranges into one table.

@boudiccamorgana
Copy link

Okay, so what does that look like in code? I'm trying to figure out how I would separate the blocks on the code - so I can have my ranges with

let sortRange = sheet.getSheetValues(2,1,lastRow, 7);
let otherRange = sheet.getSheetValues(9,1,lastRow, 6);

but I'm stuck on where to put the code to tell it where to paste?

@lcbrown1
Copy link

I am totally new to writing scripts, so thanks in advance for your patience. When I try to run this script on my master sheet, I get "TypeError: Cannot read properties of null (reading 'getLastRow')"

Any advice? Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment