Google Script: Moving row to different sheets based on multiple conditions - google-apps-script

I've created a spreadsheet that I'd like to separate based on the data found in one particular cell. The main spreadsheet has a column that indicates either "tardy, discipline, reinforcement." I'd like to move "tardy" and "reinforcement" into their own spreadsheets. Additionally, I'd like "tardy" and "reinforcement" lines to be removed, once in the appropriate sheet. This should give me a total of three spreadsheets.
I've used a script before that was working and moving my tardy lines onto a different spreadsheet. However, for whatever reason, I am not getting an error that says "TypeError: Cannot read property 'length' of undefined (line 19, file "Code").
I've been playing around with this and it's likely I've messed something up. I have no experience coding but I'm trying my best to figure this out. Currently, the script I have is below. Any help would be greatly appreciated.
function copyrange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Minor Infractions - Current'); //source sheet
var testrange = sheet.getRange('O:O'); //range to check
var testvalue = (testrange.getValues());
var csh = ss.getSheetByName('Tardies - Current'); //destination sheet
var data = [];
var j =[];
//Condition check in O:O; If true copy the same row to data array
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Tardy') {
data.push.apply(data,sheet.getRange(i+1,1,1,25).getValues());
//Copy matched ROW numbers to j
j.push(i);
}
}
//Copy data array to destination sheet
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
//Delete matched rows in the source sheet
for (i=0;i<j.length;i++){
var k = j[i]+1;
sheet.deleteRow(k);
//Alter j to account for deleted rows
if (!(i == j.length-1)) {
j[i+1] = j[i+1]-i-1;
}
}
}

as this answer says, you could accomplish this with a function:
=sort(importrange("spreadsheetURL", "Sheet1!A2:AA10000"),sort_col#,TRUE/FALSE,[sort_col2#],[TRUE/FALSE]...)
keep in mind this function is a guide, you can't just copy and paste, you have to fill out the values

Try this:
function copyrange() {
const ss=SpreadsheetApp.getActive();
const sheet=ss.getSheetByName('Minor Infractions - Current');
const testrange=sheet.getRange(1,15,sheet.getLastRow());
const testvalue=testrange.getValues();
const csh=ss.getSheetByName('Tardies - Current');
let data=[];
let d=0;
for (let i=0;i<testvalue.length;i++) {
if (testvalue[i]=='Tardy') {
data.push(sheet.getRange(i+1,1,1,25).getValue());
sheet.deleteRow(i+1-d++);
}
}
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
}

Related

TypeError: Cannot reap property 'length' of undefined

semi rookie here
I'm trying to run a script to copy a row from one spreadsheet to another - it works initially, then it comes up with a TypeError.
TypeError: Cannot read property 'length' of undefined
copyrange1 # onedit destination.gs:26
onEdit # onedit destination.gs:2
I'm looking to multiple functions with three destination sheets based on the value in cell AQ then for it to delete the row from the source sheet.
Any suggestions?
function onEdit(){
copyrange1();
copyrange2();
copyrange3();
}
function copyrange1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Position Activity'); //source sheet
var testrange = sheet.getRange('AQ:AQ'); //range to check
var testvalue = (testrange.getValues());
var csh = ss.getSheetByName('Suspended Pending'); //destination sheet
var data = [];
var j =[];
//Condition check in AQ:AQ; If true copy the same row to data array
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Suspended - Pending') {
data.push.apply(data,sheet.getRange(i+1,1,1,43).getValues());
//Copy matched ROW numbers to j
j.push(i);
}
}
//Copy data array to destination sheet
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
//Delete matched rows in the source sheet
for (i=0;i<j.length;i++){
var k = j[i]+1;
sheet.deleteRow(k);
//Alter j to account for deleted rows
if (!(i == j.length-1)) {
j[i+1] = j[i+1]-i-1;
}
}
}
Thanks in advance
Issue:
data will only be populated if there are rows in AQ:AQ in which the cell value is 'Suspended - Pending'.
Since the script is deleting the rows where the cell value is 'Suspended - Pending' after copying them, data will remain an empty array [] in successive executions.
If data is empty, data[0] is undefined, so it doesn't have a length.
Solution:
Make sure that data is populated before using csh.getRange(...).setValues(data);.
function copyrange1() {
// ...stuff...
if (data.length) {
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
// ...more stuff...
}
}

Apps script copy multiple range from 1 sheet to another spreadsheet

I am trying to copy data from 1 spreadsheet to another, I have successfully implemented something i found online that works with a specific range
function cloneGoogleSheet() {
// source doc
var sss = SpreadsheetApp.openById("spreadsheetkey1");
// source sheet
var ss = sss.getSheetByName('_tab_name_source');
// Get full range of data
var SRange = ss.getRange(7,3,5,1);
// get A1 notation identifying the range
var A1Range = SRange.getA1Notation();
// get the data values in range
var SData = SRange.getValues();
// target spreadsheet
var tss = SpreadsheetApp.openById("spreadsheetkey2");
// target sheet
var ts = tss.getSheetByName('tab_name_destination');
// Clear the Google Sheet before copy
//ts.clear({contentsOnly: true});
// set the target range to the values of the source data
ts.getRange(A1Range).setValues(SData);
};
The above piece coding work perfectly however I need to copy 18 different ranges that i cant just merge into 1 range. I considered the option of using the above however "multiplying" it 18 times for each range that however seems like a very inelegant solution.
I found a working solution that works if it stays within the same spreadsheet since it uses copyto instead of get/set values. The values part works perfectly since it doesnt mess with merge cells formatting. I have been struggling past 2-3 hours in merging the below-working code with elements from the first code to make a working script.
function test () {
try {
var spread = SpreadsheetApp.openById("spreadsheetkey");
var sheet = spread.getSheetByName("tab_name_source");
var rlist = sheet.getRangeList(["c7:c11", "g7:g11", "k7:k11"]);
sheet = spread.getSheetByName("tab_name_destination");
for( var i=0; i<rlist.getRanges().length; i++ ) {
var r1 = rlist.getRanges()[i];
var r2 = sheet.getRange(r1.getA1Notation());
r1.copyto(r2);
}
}
catch(err) {
Logger.log(err);
}
}
I tried initially to adapt the 2nd piece of coding to using setvalues however i had not been able to succesfully implement the part of getvalues within the scope of this code. I figured once I got this piece of code working with get and set values instead of Copyto i would only need to add the spreadsheetid of the other spreadsheet to get the final result
Try this:
function myFunction() {
var sourceSS = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = sourceSS.getSheetByName("sheetname");
var targetSS = SpreadsheetApp.openById("spreadsheet id here");
var targetSheet = targetSS.getSheetByName("Sheet1");
var ranges = ["C7:C11", "G7:G11", "K7:K11"];
ranges.forEach(range => {
var data = sourceSheet.getRange(range).getValues();
targetSheet.getRange(range).setValues(data);
})
}
Source sheet:
Destination sheet:
References:
setValues()
getValues()

Fetch data from source sheet based on emails provided in the Emails tab in the target spreadsheet

I have a problem where I have two sheets. one sheet is the source spreadsheet and another is a target spreadsheet. The source spreadsheet has a source sheet has which is the master database and the target spreadsheet has the target where we want to fetch data from source sheet based on emails provided in the Emails tab in the target spreadsheet.
I want the following things to happen with a script and not with IMPORTRANGE or QUERY:
The target spreadsheet will have multiple copies so I want to connect the target spreadsheet with the source spreadsheet based on the source spreadsheet's id.
I want the email matches to be case insensitive so that the users of the target spreadsheet can type emails in any case.
The Emails can go up to 50 or let's say get the last row for that column.
It will be great if the script shows a pop up saying updated after it has fetched the data.
The source sheet might have data up to 15000 rows so I am thinking about speed too.
I have shared both of the spreadsheets with hyperlinks to their names. I am not really great at scripts so it will be helpful if you can leave comments in it wherever you feel like. I would truly appreciate your help.
Thanks in advance!
Script here:
function fetch() {
//get the sheets
var source_Ssheet = SpreadsheetApp.openById('19FkL3rsh5sxdujb6x00BUPvXEEhiXfAeURTeQi3YWzo');
var target_Ssheet = SpreadsheetApp.getActiveSpreadsheet();
//get the tabs
var email_sheet = target_Ssheet.getSheetByName("Emails");
var target_sheet = target_Ssheet.getSheetByName("Target Sheet");
var source_sheet = source_Ssheet.getSheetByName("Source Sheet");
//get ranges
var email_list = email_sheet.getRange("B2:B");
var target_sheet_range = target_sheet.getRange("A1:F100");
var source_sheet_range = source_sheet.getRange("A1:F100");
//get last rows
var last_email_name = email_list.getLastRow();
var last_target_sheet_range = target_sheet_range.getLastRow();
var last_source_sheet_range = source_sheet_range.getLastRow();
//start searching for emails
for (var i=3; i < last_email_name.length+1; i++)
{
for(varj=3; j< last_source_sheet_range.length+1; j++ )
{
if(source_sheet_range[j][3].getValue() == email_list[i][3].getValue())
{
//copy matches to target sheet
target_sheet.getRange((last_target_sheet_range + 1),1,1,10).setValues(master_sheet_range[j].getValues());
}
}
}
}
Several things
last_email_name and last_source_sheet_range are numbers - they do not have any length, this is why your first forloops are not working
You are missing a space in varj=3;
email_list[i][3].getValue() does not exist because email_list only includes B - that only one column. I assume you meant email_list[i][0].getValue()
ranges cannot be addressed with the indices [][], you need to retrieve the values first to have a 2D value range.
You email values in the different sheets do not follow the same case. Apps Script is case sensitive, to suee the == comparison you need to use the toLowerCase() method.
Also mind that defining getRange("B2:B") will include many empty rows that you don't need and will make your code very slow. Replace it through getRange("B2:B" + email_sheet.getLastRow());
Have a look here at the debugged code - keep in mind that there is still much room for improvement.
function fetch() {
//get the sheets
var source_Ssheet = SpreadsheetApp.openById('19FkL3rsh5sxdujb6x00BUPvXEEhiXfAeURTeQi3YWzo');
var target_Ssheet = SpreadsheetApp.getActiveSpreadsheet();
//get the tabs
var email_sheet = target_Ssheet.getSheetByName("Emails");
var target_sheet = target_Ssheet.getSheetByName("Target Sheet");
var source_sheet = source_Ssheet.getSheetByName("Source Sheet");
//get ranges
var email_list = email_sheet.getRange("B2:B" + email_sheet.getLastRow()).getValues();
var target_sheet_range = target_sheet.getRange("A1:F100").getValues();
var source_sheet_range = source_sheet.getRange("A1:F100").getValues();
var last_target_sheet_range = target_sheet.getLastRow();
//start searching for emails
for (var i=1; i < email_list.length; i++)
{
for(var j=1; j< source_sheet_range.length; j++ )
{
if(source_sheet_range[j][0].toLowerCase() == email_list[i][0].toLowerCase())
{
target_sheet.getRange((last_target_sheet_range + 1),1,1,6).setValues([source_sheet_range[j]]);
}
}
}
}

Issue with ss.insertSheet - inserted sheet has wrong name

Hello I wrote a small script to copy one template sheet in a spreadsheet, as a new sheet in the same spreadsheet.
I wrote two versions of it, one driven by a menu that asks for the name of the new sheet to be created:
function addonenewSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var temp = ss.getSheetByName('template');
var naame = Browser.inputBox("CustomerID to be created");
try {
ss.setActiveSheet(ss.getSheetByName(naame));
}
catch (e) {
ss.insertSheet(naame, {template:temp});
}
}
This one works as intended, and names the new sheet 234 if I say so in the inputbox.
The second function is very similar, but parses some values and attempts to create many sheets at once:
function addmissingSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var temp = ss.getSheetByName('template');
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 10; i < data.length; i++) {
if(typeof data[i][1] == 'number'){
try {
ss.setActiveSheet(ss.getSheetByName(data[i][1]));
}
catch (e) {
Logger.log('Customer ID: ' + data[i][1]);
var insertpage = data[i][1];
ss.insertSheet(insertpage, {template:temp});
}
}
}
}
As long as Logger.log is concerned, data[i][1] has the right value, but somehow insertSheet creates sheets named "copy of template", "copy of template 2"... Instead of taking the value assigned in data[i][1]
Would anyone know why this behaviour and how I can solve this issue?
your second script does not use correct variable types. The method you are using insert sheet uses types (<string>, {template:<sheet>}). Since your customer ID is a number it does not work. There is a simple fix you can do
Change
var insertpage = data[i][1];
into:
var insertpage = data[i][1].toString();
and you will now be able to use the customer ID (which is a number) to create a sheet name (which is a string)

Need help Copying and Editing sheet within the same Spreadsheet in Google Apps Script

I need some help refining this code. It should copy a specific worksheet in this case the sheet Master Copy based on the rows on the sheet Maxes. Then rename the sheet to the name on the given row in the sheet Maxes. When I try to execute the code all it does is insert a blank page and adds it to the end of the Spreadsheet. Please forgive the poorly written code I am really new at this and trying to make something work. Any suggestions that you could give would be gratefully appreciated. I'm not sure if there is an error within the while loops or what exactly.
function createWorkouts(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var nameSource = ss.getSheetByName("Maxes");
var nameColumn = nameSource.getRange("A2:A300");
var nameStartRow = nameSource.getRange("A2");
var trainingSheet = ss.getSheetByName("Master Copy");
var trainingRange = trainingSheet.getRange("a1:m66");
var nameEndRow = nameSource.getLastRow();
var lifterName;
while (nameStartRow <= nameEndRow)
{
lifterName = nameSource.getCell(nameStartRow, nameColumn);
if (lifterName == null)
{
errors.clear();
ss.getSheetsByName(lifterName) = lifterName;
if(errors.number() > 0)
{
errors.clear();
var newSheet = ss.insertSheet();
ss.renameActiveSheet(lifterName);
ss.getSheetByName(lifterName);
trainingRange.copyValuesToRange(lifterName,1,1,16,66);
lifterName.copyValuesToRange(lifterName,4,1,4,1);
}
} nameStartRow = nameStartRow + 1;
}
}
I modifyed your code because I didn't understood what you wanted exactlly, I hope this will do the trick:
function createWorkouts(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var nameSource = ss.getSheetByName("Maxes");
var nameColumn = nameSource.getRange("A2:A").getValues(); // it's a table of table [][]
var trainingSheet = ss.getSheetByName("Master Copy");
var trainingRange = trainingSheet.getRange("A1:M66").getValues(); // get values give a table of table
for(var i in nameColumn){
if(nameColumn[i][0]!=""){ // if blank we skip
try{ // try to see if already exist who know...
var lifter = ss.getSheetByName(nameColumn[i][0]).activate();
Logger.log("getting lifter sheet "+nameColumn[i][0]);
}
catch(err){ // well it didn't exist so lets create it :D
var lifter = ss.insertSheet(nameColumn[i][0]);
Logger.log("creating lifter sheet "+nameColumn[i][0]);
}
SpreadsheetApp.flush();
lifter.getRange("A1:M66").setValues(trainingRange); // passing the value to the created sheet
}
}
}
I also have a demo sheet here.
Please tell me if this is what you wanted (if not can you give a demo sheet of what you want and what you should get at the end)
EDIT
here the script with formulas copy (it's not nice because you are forced to define in the script where your formulas are, we could have the script to analyse the spreadsheet to search the formulas, but, has it's a copy from template, it's less ressource consuming not to do so.)
function createWorkouts(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var nameSource = ss.getSheetByName("Maxes");
var nameColumn = nameSource.getRange("A2:A").getValues(); // it's a table of table [][]
var trainingSheet = ss.getSheetByName("Master Copy");
var trainingRange = trainingSheet.getRange("A1:H21").getValues(); // get values give a table of table
var trainingFormulasD = trainingSheet.getRange("D2:D21").getFormulas(); // get the formulas to copy
var trainingFormulasH = trainingSheet.getRange("H2:H21").getFormulas(); // get others formulas to copy
for(var i in nameColumn){
if(nameColumn[i][0]!=""){ // if blank we skip
try{ // try to see if already exist who know...
var lifter = ss.getSheetByName(nameColumn[i][0]).activate();
Logger.log("getting lifter sheet "+nameColumn[i][0]);
}
catch(err){ // well it didn't exist so lets create it :D
var lifter = ss.insertSheet(nameColumn[i][0]);
Logger.log("creating lifter sheet "+nameColumn[i][0]);
}
SpreadsheetApp.flush();
lifter.getRange("A1:H21").setValues(trainingRange); // passing the value to the created sheet
lifter.getRange("D2:D21").setFormulas(trainingFormulasD); //passing formulas
lifter.getRange("H2:H21").setFormulas(trainingFormulasH); //passing formulas
}
}
}