Use script to update column but only to last row - google-apps-script

I am trying to have a script run that will basically find the row id in my spreadsheet then copy and paste that ID as a value instead of the formula. My solution feels clunky and I've run into a couple of issues.
The main issue is that I only want the script to update active rows (ie rows that have content in them), which is variable. This sheet is updated constantly throughout the day, and I really only want it to find and write the row id once per day (basically, these are tasks to be handed out and the row id sets priority). My plan is once I get the find row id/paste value scripts going, I'll use a trigger to run daily.
I'm a bb when it comes to coding and understanding scripts, so maybe explain it to me like im 5 if you can.
function getRowId(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var cell = sheet.getRange("I2:I");
cell.setFormula("=row()");
}
function pasteValue(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("Sheet1");
var sheet = ss.getSheets();
var range = sourceSheet.getRange("I2:I");
range.setValues(range.getValues());
}

You can do the whole process in just one function. Here is the code I tested and worked successfully pasting the correlated row number in the "I" column of each row.
function pasteValue(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("Sheet1");
var range = sourceSheet.getDataRange().getValues();
for(var i=0; i<(range.length - 1); i++) {
var cell = sourceSheet.getRange(2 + i, 9);
cell.setValue(2 + i);
}
}
This code will do it only for the range of rows where is data already present, because it's using the getDataRange() function to make it more efficient. If you want to set the row number for all the rows present (even if they don't have any data), you need to change the declaration of the "range" variable to this:
var range = sourceSheet.getRange("I2:I");

Related

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]);
}

"Service Spreadsheets failed while accessing" on (seemingly) simple code

Does anyone have an idea of why my code is throwing the "Service Spreadsheets failed while accessing" error? I've read that this is usually caused by a large dataset, but all the datasets involved here are tiny. For whatever reason, the error is being thrown on line 6. I'm new to both Apps Script and Javascript, but I don't think this code should take very long to run at all. The function simply aims to take values from one sheet and drop them into their corresponding place on another sheet -- I've used formulas in the sheet to find the column number (that is the column_nums var) although ideally I would find an all-script solution. The reason I'm doing this through script and not a simple index match is because I want the values from sheet A to be updated over time and use sheet B to periodically (on a trigger) paste in sheet A's values to track them over time. Apologies if this is a basic question, thanks so much!
function export_maxes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
var new_maxes = source.getRange("E1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
var column_nums = source.getRange("A1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
// find row
var row_num = destination.getRange("B1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getHeight();
var row_num = row_num + 1;
// find columns and input maxes. start at 1 bc data has headers
for (var i = 1; i < new_maxes.length; i++) {
destination.getRange(row_num, column_nums[i]).setValue(new_maxes[i])
}
};
There is ongoing issue tracker for "getDataRegion failed when it faces hidden rows or columns". Alternative solution is to show the hidden column groups by using method:expandAllColumnGroups() and hide the column after you fetched the data by using method:collapseAllColumnGroups()
Your code should look like this.
function export_maxes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
source.expandAllColumnGroups();
var new_maxes = source.getRange("E1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
var column_nums = source.getRange("A1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getValues();
// find row
var row_num = destination.getRange("B1").getDataRegion(SpreadsheetApp.Dimension.ROWS).getHeight();
source.collapseAllColumnGroups();
var row_num = row_num + 1;
// find columns and input maxes. start at 1 bc data has headers
for (var i = 1; i < new_maxes.length; i++) {
destination.getRange(row_num, column_nums[i]).setValue(new_maxes[i])
}
};
References:
expandAllColumnGroups
collapseAllColumnGroups

Range Length in Google Apps Script

I want to run a script that copies a sheet data to a master sheet (append all my sheets).
The first part of copying and pasting is working but I want to add a column which tells me the name of the origin sheet. I wrote a loop for it but nothing is happening when I executing the script (only the copy and paste). This is my whole code:
function appendSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var reportLastRow = sh.getLastRow()
var reportLastColumn = sh.getLastColumn()
var reportData = sh.getSheetValues(3,1,reportLastRow,reportLastColumn);
var recordsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("2020 Data");
var lastRow = recordsSheet.getLastRow();
//var recordLastRow = sh.getLastRow();
var recordLastColumn = recordsSheet.getLastColumn();
var reportSheetName = sh.getSheetName();
recordsSheet.getRange(lastRow + 1,1,reportLastRow,reportLastColumn).setValues(reportData);
var arrayLength = (lastRow - reportData.length);
for (var i = 0 ; i <= arrayLength ; i ++) {
var taskDateCell = recordsSheet.getRange(arrayLength - i, recordLastColumn);
taskDateCell.setValues(reportSheetName);
}
}
Your code has several problems you should fix
Be aware of the fact that the method getRange() expects the syntax firstRow, firstColumn, numRows, numColumns - not firstRow, firstColumn, lastRow, lastColumn. You need to adjust your range and your function getSheetValues() (I assume it is you custom funciton based on the method getRange() accordingly.
If you assign a value to one cell at a time taskDateCell, you should use setValue() instead of setValues()
It seems like your definition of arrayLength might not be right. Test it by logging it.
Your main problem:
You define:
for (var i = arrayLength ; i < arrayLength ; i ++)
In other words:
Set i to arrayLength and iterate while i is smaller than arrayLength.
This condition is never fullfilled, and thus the number of iterations will be zero.
As a genral advice: Implement in your code many logs - to visualize important values, such a range notations and length of arrays, or counter variables in a loop - this will help you to find bugs faster.
Explanation:
First of all I optimized your code. You have unnecessary lines of code which make your code difficult to be understood but also slow. The optimizations involve reducing the number of lines to the minimum, defining constant variables, making the variable names more descriptive and finally getting rid of the for loop by replacing it with a more efficient approach.
Another correction would be at this line: sh.getSheetValues(3,1,reportLastRow,reportLastColumn); Here you are starting from the third row but you are getting two rows extra; reportLastRow is the number of rows you want to get but since you are starting from the third row you need to deduct 2.
To answer your question, one way to solve your problem is to set the name of the report to the last column of the records sheet where you entered the data from the report sheet. Since you want to add the same value (sheet name) to every row, you can select a 2D range but use setValue.
I am not a big fan of getActiveSheet in const report_sh = ss.getActiveSheet(); since this line is assuming that you have selected the desired sheet (report sheet) in the UI. Please be careful with that, otherwise change that line to something like that:
const report_sh = ss.getSheetByName('report sheet');
and of course adjust 'report sheet' to the name of the sheet you want to append.
Solution:
function appendSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const records_sh = ss.getSheetByName("2020 Data");
const report_sh = ss.getActiveSheet();
const reportData = report_sh.getSheetValues(3,1,report_sh.getLastRow()-2,report_sh.getLastColumn());
const records_lr = records_sh.getLastRow();
records_sh.getRange(records_lr+1,1,reportData.length,reportData[0].length).setValues(reportData);
SpreadsheetApp.flush();
records_sh.getRange(records_lr+1,records_sh.getLastColumn()+1,reportData.length,1).setValue(report_sh.getSheetName());
}

How to make google sheets index from values in column and hyperlink those to sheets

I have a bunch of data I want to put in to multiple sheets, and to do it manually would take time and I would like to learn scripting too.
So say I have a sheet with the states in one column.
I would like to have a script make new sheets based off the values of that column, and make a hyperlink to those sheets, and sort the sheets alphabetically.
In each sheet, I need to have the A1 cell the same name as the sheet.
Here is an example of states
Any suggestions would be helpful
Edit:
This is code that can make sheets based on the values of the columns.
function makeTabs() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var last = sheet.getLastRow();//identifies the last active row on the sheet
//loop through the code until each row creates a tab.
for(var i=0; i<last; i++){
var tabName = sheet.getRange(i+2,1).getValue();//get the range in column A and get the value.
var create = ss.insertSheet(tabName);//create a new sheet with the value
}
}
(note the "sheet.getRange(i+2,1" assumes a header, so pulls from the first column, starting on the second row)
I still need to:
Add a hyper link in the index sheet to the State's sheet: example: A2 on the Index sheet
would be =HYPERLINK("#gid=738389498","Alabama")
Also I need the A1 cell of the State's page to have the same info as
the index. example: Alabama's A1 cell would be =Index!A2
You could take a look at this script:
function createSheets(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var indexSheet = ss.getSheetByName('Index');
var indexSheetRange = indexSheet.getDataRange();
var values = indexSheetRange.getValues();
var templateSheet = ss.getSheetByName('TEMPLATE_the_state');
templateSheet.activate();
var sheetIds = [],
state,
sheetId,
links = [];
for (var i = 1 ; i < values.length ; i++){
state = values[i][0];
sheetId = undefined;
try{
var sheet = ss.insertSheet(state, {template: templateSheet});
SpreadsheetApp.flush();
sheet.getRange("A1:B1").setValues([['=hyperlink("#gid=0&range=A' +(i+1)+'","go back to index")',state]]);
sheetId = sheet.getSheetId();
}
catch (e) { Logger.log('Sheet %s already exists ' , sheet)}
sheetIds.push([sheetId,state]);
}
sheetIds.forEach(function(x){
links.push(['=HYPERLINK("#gid='+x[0]+'&range=A1","'+x[1]+'")']);
});
indexSheet.getRange(2,1,links.length,links[0].length).setValues(links) // in this case it is clear to us from the outset that links[0].length is 1, so we could have written 1
}
Note that in my version, I created a template sheet from which to base all the state sheets from. This wasn't what you asked for, but I wanted to see what it would do.
The resulting sheet is here: https://docs.google.com/spreadsheets/d/1Rk00eXPzkfov5e3D3AKOVQA2UdvE5b8roG3-WeI4znE/edit?usp=sharing
Indeed, I was surprised at how long it took to create the full sheet with all the states - more than 250 secs. I looked at the execution log, which I have added to the sheet in its own tab. There it is plain to see that the code is quick, but sometimes (why only sometimes, I don't know) adding a new tab to the spreadsheet and/or flushing the formulas on the spreadsheet is very slow. I don't know how to speed it up. Any suggestions welcome. (I could try the Google Sheets API v4, probably would be much faster ... but that is much more work and tougher to do.)

Copy a row with specific cells to another spreadsheet

I have been searching for a way to copy certain cells from a row and paste them into another spreadsheet, but all I can seem to find are ways to do that just from sheet to sheet within one spreadsheet or questions that deal with code way above my head. I'm just now learning to code in Google Spreadsheets and I can't seem to get it right. Here is my code for now, I'm working on just copying one cell first and after I can do that I'll get a loop to iterate through the row and pick the cells I want to copy.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var master = SpreadsheetApp.openById('0AgcCWQn-aoI1dFpLVE4tSENwcThrYnlMUzhuRmdWU2c');
var target = SpreadsheetApp.openById('0AgcCWQn-aoI1dF96X2dBT2dVVFZ2SU1NRWdYTDJhT2c');
var master_sheet = master.getSheetByName("Steve2");
var target_sheet = target.getSheetByName("Corbin1");
var master_range = master_sheet.getRange("A1");
var target_range = target_sheet.getRange("A1");
master_range.copyTo(target_range);
Right now it is giving me an error saying that it cannot call the getRange method of null. I'm not sure if I'm using OpenById correctly or if I can even use that in this situation.
OK I've got it working now. It copies the row perfectly and it copies only the cells I want. Now I need help with the pasting part. It copies everything fine and puts it into the other sheet great, but I need it to copy into the next available row and into the first available columns. So if row 5 is the last column of data, I need it to paste into row 6 and take up the first 6 or so columns.
This is what I have so far.
function menuItem1() {
var sss = SpreadsheetApp.openById('0AgcCWQn-aoI1dFpLVE4tSENwcThrYnlMUzhuRmdWU2c'); // sss = source spreadsheet Steve2
var ss = sss.getSheetByName('Sheet1'); // ss = source sheet
//Message box asking user to specify the row to be copied
var answer = Browser.inputBox('What row would you like to copy?');
var range = parseInt(answer);
var array = [1,2,3,9,12,30];
//Runs a loop to iterate through array, using array elements as column numbers
for(var i = 0; i < array.length; i++){
var SRange = ss.getRange(answer,1,1,array[i]);
//get A1 notation identifying the range
var A1Range = SRange.getA1Notation();
//get the data values in range
var SData = SRange.getValues();
}
var tss = SpreadsheetApp.openById('0AgcCWQn-aoI1dF96X2dBT2dVVFZ2SU1NRWdYTDJhT2c'); // tss = target spreadsheet Corbin1
var ts = tss.getSheetByName('Sheet1'); // ts = target sheet
//set the target range to the values of the source data
ts.getRange(A1Range).setValues(SData);
//Confirmation message that the row was copied
Browser.msgBox('You have successfully copied Row: ' + answer);
}
You are getting this error maybe because your spreadsheet does not have a sheet called Steve2 or Cobin1. Try using the method master.getSheets()[0], this way you will get the first sheet without using their name.
You can algo use this piece of code to check the sheets names:
for(var x in master.getSheets()) {
Logger.log(master.getSheets()[x].getSheetName());
}
best,