Trying to retain formatting in a sheet with multiple users - google-apps-script

Very new at this and I'm probably doing something very basic wrong but I hope someone can help.
I'm trying to preserve the formatting of a sheet that is open for editing by multiple users. I'm using the onEdit() trigger and using a second sheet (formatSheet) to restore the formatting when a user changes the values.
I'm working through this piecemeal checking that each line works so this is not complete (ie I still need to ensure data validation and conditional formatting is retained, I'm not sure that copyFormatToRange does this). However the copyFormatToRange command does not work, script stops executing on this line.
// Retain Formatting
var formatSheetName = "Master Format";
var formatSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(formatSheetName);
var watchSheetRange = SpreadsheetApp.getActiveSpreadsheet().getActiveRange();
var formatSheetRange = formatSheet.getRange(watchSheetRange.getA1Notation());
var firstRow = formatSheetRange.getRow();
var lastRow = formatSheetRange.getLastRow();
var firstColumn = formatSheetRange.getColumn();
var lastColumn = formatSheetRange.getLastColumn();
var gridID = watchSheetRange.getGridId();
watchSheetRange.copyFormatToRange(gridID,firstRow,firstColumn,lastRow,lastColumn);
I've checked that watchSheetRange is in fact a Range (it is) that gridID is a gridID (it is), that the row/column variables are integers (they are).
Any help appreciated.

This works
function lfunko() {
const ss = SpreadsheetApp.getActive();
const fsh = ss.getSheetByName("Sheet0");
const wsh = ss.getSheetByName("Sheet1")
const frg = fsh.getRange("A3:S3");//cols 1 to 19
let wrg = wsh.getActiveRange();
let id = wrg.getGridId();
frg.copyFormatToRange(id, 1, 19, wrg.getRow(), wrg.getRow());
}
I'm using the same columns and whatever the active row and I'm only doing one row. The params for the copyFormatToRange are id, columnStart, columnEnd, rowStart, rowEnd

Have you considered creating a form instead of sharing a spreadsheet for data input? Form responses automatically appear in a separate tab in the spreadsheet in a row-oriented fashion and are thus easy to process with spreadsheet functions such as query() and filter().

Related

Copying Multiple Rows from One Sheet to A Target - Google Apps Script (...is not a function error)

I am trying to write a script that will copy specific rows on a general source sheet and paste it to a target sheet. I know that there is a lot of examples of this online but there are some variations that do not work with my code, so it is difficult for me to apply (a novice).
Here is the code that I have written:
function singleSheetIOD() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var fillDownRange = ss.getRange(2, 19, lr-3);
ss.insertColumnAfter(18);
ss.getRange(1,19,1,1).setValue('IOD');
ss.getRange("S2").setFormula("=H2*R2");
ss.getRange("S2").copyTo(fillDownRange);
const netSquare = ss.getRange('S2:S').getValues().flat().filter(v=>v!='');
const sum = netSquare.reduce((a,b)=>a+b);
ss.getRange(ss.getLastRow(), 20, 1, 1).setValue(sum);
const value1 = ss.getRange(lr, 20).getValue();
const value2 = ss.getRange(lr,9).getValue();
const divisor = value1/value2;
ss.getRange(lr,11,1,1).setValue(divisor);
%below this is the actual "copy and paste" attempt
var lastTwoRows = ss.getRange(lr-1,1,2,11).getValues();
var pastesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Summary');
pastesheet.setValues(lastTwoRows);
}
I keep receiving this error: TypeError: pastesheet.setValues is not a function
It is important that I do not define the source Sheet by name (only the target sheet) because this is nested in a for loop that will loop across all other sheets in the spreadsheet.
Does anyone have an idea why I might be receiving this error?
Thank you so much.
AEB
You must define the range of the sheet you're looking to setValues on.
// Currently:
pastesheet.setValues(lastTwoRows);
// Needs to be:
pastesheet.getRange(...).setValues(lastTwoRows);
As of right now, you're trying to set the values on a sheet, but have not specified where to put them.
Note: The range must be equal to the size of the array you're setting.
See:
.getRange(row, column, numRows, numColumns)
.getRange(a1Notation)

transferring data from one sheet to another one with script [duplicate]

sure I had this down but just can't get it right. Had a script set up to get data from one sheet and put it into another one, which I have done but it leaves gaps when copying and I can't figure out how to solve it. I'm sure in the code, its where I have put a question mark, is where the problem lies.
I have tried put i, last, last+1, 10, 12 but none of these work, feel like i'm missing something small to get this right. Below is the code a link to view the sheet if needed (the sheet is just for me to learn from, a basic example if you will).
Thanks in advance and also if the code could be better written, please just let me know as still learning this :)
function copyInfo() {
var app = SpreadsheetApp;
var copySheet = app.getActiveSpreadsheet().getSheetByName("Copy");
for (var i = 2; i <12; i++) {
var getInfo = copySheet.getRange(2,2,i,2).getValues();
// get the info from range above - start at row 2 on column 2 (b), get number of rows i , number of columns = 2, b,c
var last = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Paste").getLastRow();
var pasteSheet = app.getActiveSpreadsheet().getSheetByName("Paste");
// Tell it where you want the info to go to
pasteSheet.getRange(last+1,1,?,2).setValues(getInfo);
var clearIt = copySheet.getRange(2,2,i,2).clearContent();
// this clears the copy range aka getInfo
}}
link to sheet
You can copy whole range at once using copyTo, so your function could be rewritten as:
function copyInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
// get source range
var source = copySheet.getRange(2,2,12,2);
// get destination range
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,2,12,2);
// copy values to destination range
source.copyTo(destination);
// clear source values
source.clearContent();
}

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

Improving Apps Script flexibility by using a column of sheet data instead of hard-coded IDs

Background: My coworkers originally each had a worksheet within the same Google Sheets file that makes a lot of calculations (and was getting unusable). Now, everyone has their own (known) Google Sheets file. To run the same calculations, we need to consolidate all that data into a master sheet (image ref below). We tried =importrange(...), but it's too heavy and breaks often (i.e., Loading... and other unfilled cells).
I've written some code to do this import, but right now its only manual: manually repeating the code and manually add the sheet IDs and changing the destrange.getRange(Cell range) each time. We have 80+ analysts, and fairly high turnover rates, so this would take an absurd amount of time. I'm new to Sheets and Apps Script, and know how to make the script use a cell as reference for a valid range or a valid ID, but I need something that can move a cell down and reference the new info.
Example:
Sheet 1 has a column of everyone Sheet ID
Script Pseudocode
get first row's id(Row 1), get sheet tab, get range, copies to active sheet's corresponding row(Row 1).
gets second row's id(Row 2), get sheet tab, get range, copies to active sheet's corresponding row (Row 2)
etc.
My script understanding is way to low to know how to process this. I have no idea what to read and learn to make it work properly.
function getdata() {
var confirm = Browser.msgBox('Preparing to draw data','Draw the data like your french girls?', Browser.Buttons.YES_NO);
if(confirm == 'yes'){
// I eventually want this to draw the ID from Column A:A, not hard-coded
var sourcess = SpreadsheetApp.openById('1B9sA5J-Jx0kBLuzP5vZ3LZcSw4CN9sS6A_mSbR9b26g');
var sourcesheet = sourcess.getSheetByName('Data Draw'); // source sheet name
var sourcerange = sourcesheet.getRange('E4:DU4'); // range
var sourcevalues = sourcerange.getValues();
var ss = SpreadsheetApp.getActiveSpreadsheet(); //
var destsheet = ss.getSheetByName('Master Totals'); //
// This range needs to somehow move one down after each time it pastes a row in.
var destrange = destsheet.getRange('E4:DU4');
destrange.setValues(sourcevalues); // Data into destsheet
}
}
Any suggestions are greatly appreciated!
Thanks to tehhowch for pointing me in the right direction!
function getdata() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master Totals');
var confirm = Browser.msgBox('Drawing Data','Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO);
if(confirm =='yes'){
var lr = ss.getLastRow();
for (var i = 4; i<=lr; i++) {
var currentID = ss.getRange(i, 1).getValue();
var sourcess = SpreadsheetApp.openByUrl(currentID);
var sourcesheet = sourcess.getSheetByName('Data Draw');
var sourcerange = sourcesheet.getRange('E4:DU4');
var sourcevalues = sourcerange.getValues();
var destrange = destsheet.getRange('E' +i+':'+ 'DU'+ i);
destrange.setValues(sourcevalues);
I just had to learn how to use a variable loop.
Edit: thanks also to Phil for making my question more presentable!
Now that you've figured out one way to do it, I'll offer an alternative that uses batch methods (i.e. is much more time- and resource-efficient):
function getData() {
var wb = SpreadsheetApp.getActive();
var ss = wb.getActiveSheet();
var dest = wb.getSheetByName('Master Totals');
if (!dest || "yes" !== Browser.msgBox('Drawing Data', 'Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO))
return;
// Batch-read the first column into an array of arrays of values.
var ssids = ss.getSheetValues(4, 1, ss.getLastRow() - 4, 1);
var output = [];
for (var row = 0; row < ssids.length; ++row) {
var targetID = ssids[row][0];
// Open the remote sheet (consider using try-catch
// and adding error handling).
var remote = SpreadsheetApp.openById(targetID);
var source = remote.getSheetByName("Data Draw");
var toImport = source.getRange("E4:DU4").getValues();
// Add this 2D array to the end of our 2D output.
output = [].concat(output, toImport);
}
// Write collected data, if any, anchored from E4.
if(output.length > 0 && output[0].length > 0)
dest.getRange(4, 5, output.length, output[0].length).setValues(output);
}
Each call to getRange and setValues adds measurable time to the execution time - i.e. on the order of hundreds of milliseconds. Minimizing use of the Google interface classes and sticking to JavaScript wherever possible will dramatically improve your scripts' responsiveness.

Transferring a range from 1 Spreadsheet to another without overwriting the current range

I have a script I just can't seem to get quite right.
Spreadsheet1 people fill out as they have a "Hot Job", basically this creates a paper trail/transparency for the person in charge of said job.
Spreadsheet2 is more of reporting tool, how many came through their reason and from what customer.
Spreadsheet1 will get a new tab 'Sheet' each week, at the end of each week I want to send the entire range to spreadsheet2 without over writing any information that is already on spreadsheet2 via a time-driven trigger. IE getLastRow() then insert new data below.
This is where I am currently, this will do what I ask of it some of the time. Sometimes I get a "The coordinates or dimesnions of the range are invalid" error, I don't know why because sometimes it works.
function hotJob() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dropDown = ss.getSheetByName("Dropdown");
var sheetName =dropDown.getRange("H2").getValue();
var sheet = ss.getSheetByName(sheetName);
var data = sheet.getRange("A2:O").getValues();
var rowsInData = data[0].length;
var colsInData = data[0].length;
var mdURL = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1Dj34hO55q_AYISk0TxpCyVl55rSoeOGpZ1dmuJli8IE/edit#gid=2081965371');
var mdSheet = mdURL.getSheetByName("Hot Jobs");
var mdLastRow = mdSheet.getLastRow();
mdSheet.insertRows(rowsInData);
mdSheet.getRange(mdLastRow, 1, rowsInData, colsInData).setValues(data);
}
Any help would be much appreciated. I have searched and tried multiple options I have found on here, but couldn't adapt any of them to work properly for my situation either.
Thanks,
Tyler
You have two identical values here
var rowsInData = data[0].length;
var colsInData = data[0].length;
The first one shouldn't have the [0] before length.