Unable to complete data move within apps script - google-apps-script

Ive been messing with this google apps script for far too long and need some help.
I have a table on a sheet called options that starts on col A line 31 and is 3 col wide.
Col a is all checkboxes. I was able to write a script that checks to see which checkboxes are checked.
For each checked box it copies that rows data in b:c into an array.
Then opens an existing tab called Worksheet and is supposed to paste them in the first empty cell it finds in column b.
function createNamedRanges() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Worksheet");
var range = sheet.getRange("B2:C");
var namedRange = ss.setNamedRange("outputRange", range);}
function processSelectedRows() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Prompt Options");
var data = sheet.getDataRange().getValues();
var checkedRows = [];
for (var i = 30; i < data.length; i++) {
var row = data[i];
var checkbox = sheet.getRange(i + 1, 1).getValue() == true;
if (checkbox){
checkedRows.push([row[1], row[2]]);
} }
var worksheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Worksheet");
var pasteRange = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("outputRange");
pasteRange.offset(worksheet.getLastRow(), 0).setValues(checkedRows);
}
The first row on the worksheet tab are headers. The first array to copy over is 11 rows. When I ran the script. I got an error that sat there was only 1 row in the range and I had 11 rows of data. Ok, I figured I neeeded to name a range. This table will be a different size every time. So I named this range outoutRange and no matter what size i make it I get error messages.
This is my latest error message and it is hitting the very last line of code
Exception: The number of rows in the data does not match the number of rows in the range. The data has 11 but the range has 1007.
You assistance is appreciated

Modification points:
If your Worksheet is the default grid like 1000 rows and 26 columns, I think that pasteRange is all rows like 1000. I thought that this might be the reason for your current issue.
In order to retrieve the last row of the columns "B" and "C" of "Worksheet" sheet, how about the following modification?
From:
pasteRange.offset(worksheet.getLastRow(), 0).setValues(checkedRows);
To:
var lastRow = pasteRange.getLastRow() - pasteRange.getDisplayValues().reverse().findIndex(([b, c]) => b && c);
worksheet.getRange(lastRow + 1, 2, checkedRows.length, checkedRows[0].length).setValues(checkedRows);
By this modification, the values of checkedRows is put to the next row of the last row of columns "B" and "C" of "Worksheet" sheet.

Related

Google app script for loop is extremely slow

I have a pivot table set up in a google sheet file where I've labeled the sheet pivot table 1. I want to get each value of the first column of the pivot, duplicate each values 12 times in an array and paste these values in the 3rd column of sheet 5. However, it seems extremely slow to do with the script never really completing (just takes 10+ minutes before I've cancelled it).
The pivot has approximately 3000 lines, which would result in a 3000 * 12 = 36000 array.
Any thoughts on how I can optimize this?
function test2() {
// activating current spreadsheet for use
var spreadsheet = SpreadsheetApp.getActive();
//empty array
var array_dept = []
// returns (integer #) the last row of the pivot table 1 sheet that has content
var last_row = spreadsheet.getSheetByName("Pivot Table 1").getLastRow();
// Get value in pivot table 1 from range of row 1 (dept name), column 1, all the way to last row
// Then paste it in sheet5 from row 1, column 3, all the way to the last row defined above
for (var i = 1; i < last_row; i++ )
{
//get value and then paste it in a destination
var value_dept = spreadsheet.getSheetByName("Pivot Table 1").getRange(i,1).getValue();
array_dept.fill(value_dept, -12 + (12*i) , 12*i)
}
destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(1,3, last_row);
destination_dept.setValues(array_dept);
}
You don't need use a loop if you know the first row and the last row on the source column. You can just define the range:
var pivotRange = pivot.getRange(1,1,last_row)
var targetRange = target.getRange(1,3,last_row)
doc ref; this is just one of five methods to define a range.
In the OP script, there would be 3000xgetRange + 3000xgetValue. In this answer there are: 2xgetRange and 1 x getValue. This should account for a substantial amount of script processing. Of course, we know nothing of the rest of the spreadsheet (its size, formula, functions, triggers, etc). But all other things being equal, this should improve the performance of the script.
function test2() {
// activating current spreadsheet for use
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
var pivotSheetName = "Pivot Table 1"
var pivot = spreadsheet.getSheetByName(pivotSheetName)
//temporary array
var array_dept = []
// returns (integer #) the last row of the pivot table 1 sheet that has content
var last_row = pivot.getLastRow();
//Logger.log("DEBUG: last row in the pivot table:"+last_row)
var pivotRange = pivot.getRange(1,1,last_row)
// Logger.log("DEBUG: the range for the pivot range = "+pivotRange.getA1Notation())
var pivotData = pivotRange.getValues()
//Then paste it in sheet5 from row 1, column 3, all the way to the last row defined above
var targetSheetName = "Sheet5"
var target = spreadsheet.getSheetByName(targetSheetName)
var targetRange = target.getRange(1,3,last_row)
// Logger.log("DEBUG: the range for the target range = "+targetRange.getA1Notation())
targetRange.setValues(pivotData)
Logger.log("Done")
}

How to start autofill at last cell in one column to last row of adjacent column in Apps Script/Google Sheets?

So I have a table that will have data to the last row except column B. (for example, cols A, C, D etc. stop at row 40, but B stops at maybe row 25). I want to programmatically keep the series going starting at the last cell with data in B and autofill down to the last row in the spreadsheet. (Hopefully a script doing this will recognize the series and not just copy the same data to all the empty cells. When I do it manually it works.) I have something started here but I can't figure out how to call out the range of where to start the series. I get an error on line 7 "Exception: Range not found".
function fillDownFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sc = ss.getRange("B1:B").getValues();
var scr = sc.filter(String).length;
var lr = ss.getLastRow();
var fillDownRange = ss.getRange(scr,2,lr)
ss.getRange(scr).copyTo(fillDownRange);
}
In addition to the answers already provided, Apps Script already has an autoFill() method that you can use to do this. With this you just have to define a source and a destination range. This works the same as selecting the range and dragging down your mouse manually on the corner.
function fillDownFunction() {
var ss= SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var lastsheetrow = ss.getLastRow() //last row with data
//last row in the autofill range with data
var lastrangerow = ss.getRange("B1:B").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow()
var sourcerange = ss.getRange(2, 2,lastrangerow-1) //in your screenshot, this would be B2:B6
var destinationrange = ss.getRange(2, 2, lastsheetrow-1) //This would be B2:B12
sourcerange.autoFill(destinationrange, SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES)
}
Note that I skipped the header row and offset the range lengths by -1 to compensate. This is because the autofill logic uses the entire range, so it would also take into account "Item2" and repeat it every 6 rows as "Item3", "Item4" and so on. A disadvantage in this case, but may prove useful if you plan to use autofill in more complex ways in the future.
Here is one way you could have the last fill value copy down. The function grabs all of the values, maps the row that needs to fill down and then copies that last value down the rest of the sheet.
function fillDownFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tab = ss.getActiveSheet();
var sc = tab.getDataRange().getValues();
var lastRow = sc.length;
var values = sc.map(function(r) { return r[1]; }).filter(i => i);
var lastFillRow = values.length;
var fillDownValue = values.slice(-1);
tab.getRange(lastFillRow + 1, 2, lastRow - lastFillRow).setValue([fillDownValue]);
}

Google Appscript: Copy Data from a Form Sheet to Another Sheet in Last Row and Clear Form

Trying to get an Appscript to run but I can't seem to figure out the last few steps.
Background: I have a Google Sheet with two tabs, #1 is called "Data Form" which hosts a fillable form to capture Transaction information to then be input onto tab #2 called "Posted Transactions". This is a personal budget spreadsheet..
Anyways, The script below is intended to take the information input on the "Data Form", verify what the last row with data is on the "Posted Transactions" tab based on whether or not Column A has any data. (To further clarify, the "Posted Transactions" tab has formulas in columns G-I which prohibits me from using a simple "Find last Row" script.)
As it is written now, I receive an error "Exception: The number of columns in the data does not match the number of columns in the range. The data has 6 but the range has 1.
RecordNewTransaction # Code.gs:24"
Any suggestions to make this work properly?
UPDATE:
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);
After many attempts at trial and error, I needed to edit the line shown above. Current & full script is performing as expected now and is shown below. Image snips of what I was trying to accomplish are also shown for reference.
function RecordNewTransaction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Data Form"); //Form Sheet
var datasheet = ss.getSheetByName("Posted Transactions"); //Data
//Input Values
var values = [[formSS.getRange("B4").getValue(),
formSS.getRange("B6").getValue(),
formSS.getRange("B8").getValue(),
formSS.getRange("B10").getValue(),
formSS.getRange("B12").getValue(),
formSS.getRange("B14").getValue()]];
var columnToCheck = datasheet.getRange("A:A").getValues();
var lastRow = getLastRowSpecial(columnToCheck);
Logger.log(lastRow);
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);
Logger.log(dataRange);
var dataValues = dataRange.getValues();
Logger.log(dataValues);
dataRange.setValues(values);
Logger.log(dataRange.setValues(values))
formSS.getRange('B4:B14').clearContent();
};
function getLastRowSpecial(range){
var rowNum = 0;
var blank = false;
for(var row = 0; row < range.length; row++){
if(range[row][0] === "" && !blank){
rowNum = row;
blank = true;
}else if(range[row][0] !== ""){
blank = false;
};
};
return rowNum;
};
Try
var dataRange = datasheet.getRange(lastRow,1,values.length,values[0].length)
replace 1 as necessary (this is the firts column where the data will be stored
Reference:
getRange(row, column, numRows, numColumns)
See edited question above which contains the revised script needed.
The problem here was that columns G-I of my results sheet contain formulas (shown in grey) which required this line below to be modified to grab the last column -3, otherwise the script was looking at too many columns that it didn't need to. Also had to modify "lastRow" to "lastRow+1" because this kept overriding the very last line of data I already had input and the +1 will make new data go to the next available row.
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);

Check number of rows in named range and if more than 2, execute different script

Currently I have a script that will add a specified number of rows into a named range. Due to the fact that the named range will always have 1 row to start, adding in "3" rows will really only add 2. This is the expected outcome for the first time you run the script. Obviously if the script has been run already, and you are expecting to add 3 more rows, having it only add 2 is frustrating. I cannot seem to wrap my head around how this works.
Here is the functional code:
//Take the Account and number of holes from
// the sidebar and insert rows to the proper named range
function insertRowNext(account,n_rows) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ws = ss.getSheetByName('Next Week'); // Change to your sheet name
//Replace space with underscore
account=account.replace(/ /g,"_");
var nameRange = 'Next Week!'+account;
n_rows-=1;
if(n_rows>0){
//add row
var range = ss.getRangeByName(nameRange);
ws.insertRowsBefore(range.getLastRow(),n_rows);
//Show all rows in the namedRange
ws.showRows(range.getRow(),range.getNumRows()+n_rows);
}
}
Here is the code that I modified but it broke the script.
// Take the Account and number of holes from
// the sidebar and insert rows to the proper named range
function insertRowNext(account,n_rows) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ws = ss.getSheetByName('Next Week');
//Replace space with underscore
account=account.replace(/ /g,"_");
if (n_rows == 2){
n_rows -= 1
var range = ss.getRangeByName(nameRange);
ws.insertRowsBefore(range.getLastRow(),n_rows);
//Show all rows in the namedRange
ws.showRows(range.getRow(),range.getNumRows()+n_rows);
}else if (n_rows < 2){
n_rows = 0
var range = ss.getRangeByName(nameRange);
ws.insertRowsBefore(range.getLastRow(),n_rows);
//Show all rows in the namedRange
ws.showRows(range.getRow(),range.getNumRows()+n_rows);
}else{
alert("unexpected number of rows -- please check template");
//throw error
}
}
I just need the script to check how many rows are in the selected named range, and if it is 2, subtract one from the amount to add. If the amount of rows in the named range is >2, do not subtract the amount of holes.
I don't know how your sheet is organized to produce such kind of behavior when inserting rows, but if you really want to check, you can use this code block:
var range = ss.getRangeByName(nameRange);
var rangeRows = range.getNumRows();
if (rangeRows == 2) {
ws.insertRowsBefore(range.getRow(),n_rows-1);
}
else if (rangeRows > 2) {
ws.insertRowsBefore(range.getRow(),n_rows);
}
This should be the output if n_rows = 3:

How to copy a range from a mastersheet to the last row of another sheet (Another sheet name = cell value in Mastersheet Z1)

I have a mastersheet named "ENTRATE MAIN", I want to copy range "A2:J50" to the last column of another sheet whose name is found in Cell Z1 in the Mastersheet. But the code needs to check if the data is already copied first.
I'm actually new to Google App Script, so I've actually tried using some formulas but they dont do the job since the data in the master sheet is dynamic.
I have looked at the code in this URL but it doesn't exactly what I want. Copy data from one sheet to the last row of another sheet
I expect to check if the master sheet data is already there in the other sheet. I also need it to copy to the last row of the sheet name in cell Z1 of mastersheet
Maybe this is what you are looking for. Keep in mind that it's not possible to check for the Master data in the second sheet without using the name in Z1, unless you manually input the name of the second Sheet in the code first.
function main(){
//Master Sheet
var sprsheet = SpreadsheetApp.getActiveSpreadsheet();
var master_sheet = sprsheet.getSheetByName("ENTRATE MAIN");
var master_range = master_sheet.getRange("AJ2:J50");
//Second Sheet
var sheetName = master_sheet.getRange("Z1").getValue();
var second_sheet = sprsheet.getSheetByName(sheetName);
var lastrow = second_sheet.getLastRow();
var master_data = master_range.getValues();
if (lastrow > 50){ //This is in case your second sheet has less than 50 rows before copying the data
//We have to check the previous 49 rows in case the data is already there
var second_range = second_sheet.getRange("AJ"+(lastrow-49)+":J"+(lastrow));
var second_data = second_range.getValues();
if (!isCopied(master_data, second_data)){
//Data is not there so we copy it in the next 49 rows
//If you want to overwrite the last row, just remove the +1
second_range = second_sheet.getRange("AJ"+(lastrow+1)+":J"+(lastrow+49));
second_range.setValues(master_data);
}
} else {
//The second sheet has less than 50 rows so the data is obviously not there
var second_range = second_sheet.getRange("AJ"+(lastrow+1)+":J"+(lastrow+49));
second_range.setValues(master_data);
}
}
function isCopied(master_data, second_data){
for (i in master_data){
for (j in master_data[i]){
if (master_data[i][j] != second_data[i][j]){
return false;
}
}
}
return true;
}