I'm new to Google Sheets Script and got stuck trying to do the following. I have 2 columns: C containing start timecode and D containing end timecode. A third column (F) contains the length of a film cue resulting from start timecode and end timecode. Since the timecode results from a script, the cells containing the result cannot be exported to another format. So I am trying to create a 4th column that will print the results from column F as values. I managed to write a little script that can do it. It looks like this:
function getRangeValuesEP01() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getSheetByName("EP01").getRange('F3:F100').activate();
var values = range.getValues();
return values;
};
The thing is, it does not execute automatically, when I edit any of the timecodes in C or D.
So I tried this:
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet()
var sheetName = sheet.getName();
if ( sheetName == "EP02" || sheetName == "EP01" ) {
var range = sheetName.getRange('F3:F100').activate();
var r = sheetName.getActiveRange();
if (r.getColumn() == 4 ) {
var col = r.getColumn();
if ( col >= 3 && col <= 100 ) {
sheetName.getRange(r.getRow(),5);
var values = range.getValues();
}
}
}
}
It does not give me any errors, but it does not do anything either. It probably does not really make sense. As I said, I am new to those scripts. And it does not take into account that is has to look for editing in both columns C and D. Oh, or would it be enough to look at F, since that already updates its results when C or D are changed?
Maybe someone can push me into the right direction here?
Read comments:
function myonEdit(e)
{
var sheet=e.source.getActiveSpreadsheet();
var sheetName=sheet.getName();
if(sheetName=="EP02" || sheetName=="EP01")
{
var r=sheet.getRange('F3:F100');//I made changes up to this point but the next line makes no sense so rethink it in light of changes
if (r.getColumn()==4)//This line doesn't make any sense because you set range to F3:F100 F is column 6 so it will never be 4
{
var col= r.getColumn();
if ( col >= 3 && col <= 100 )
{
sheetName.getRange(r.getRow(),5);
var values = range.getValues();
}
}
}
}
Related
function namedRanges() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
for(var i=3; i<504; i++) {
var r = ss.getRange('A3:Q3'+i);
ss.setNamedRange('Song'+i, r);
}
}
The above formula, is not stepping thru the 500 song rows in my spreadsheet and naming each row, "Song 3" for row A3-Q3, etc. (starting at row 3 as I have headers in rows 1 and 2) as I expected.
So what I am trying to do is create named ranges for all 500 songs in the sheet.
What I am expecting:
Name - Range
Song3 - Songs!A3:Q3
Song4 - Songs!A4:Q4
Song5 - Songs!A5:Q5
etc.
What I am getting:
Song3 - Songs!A3:Q33
Song4 - Songs!A3:Q34
Song5 - Songs!A3:Q35
etc.
I have spent two days trying to track this down by searching (in vain). I'm sure it's easy. Anybody know how to do this?
Improved version
Below is a "new and better" version of the script I am using (which also incorporates #Casper 's answer). It is "new" and "better" because it counts the number of columns in the sheet, rather than taking a static value which makes it more flexible as it will name a range the width of the sheet, no matter that width (as measured by columns).
function namedRanges() {
var ss = SpreadsheetApp.openByUrl("url");
var sheet = ss.getActiveSheet();
//first column = 1
var numcols = sheet.getLastColumn()
Logger.log(numcols);
for(var i=101; i<501; i++) {
var r = sheet.getRange(i,1,1,numcols);//row, column, num rows, num cols
ss.setNamedRange('Song'+i, r);
}
}
You're looking for something like this I suppose:
function namedRanges() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
for(var i=3; i<504; i++) {
var r = ss.getRange('A'+i+':Q'+i);
ss.setNamedRange('Song'+i, r);
}
}
Your original code was appending the row number to your static text:
var r = ss.getRange('A3:Q3'+i);
Basically reads as:
var r = ss.getRange('A3:Q3'+3);
The + sign concatenates Q3 and 3 resulting in Q33. However, in my code you define the columns as static text and the row numbers as variable.
var r = ss.getRange('A'+i+':Q'+i);
Therefore reads as (i=3):
var r = ss.getRange('A'+3+':Q'+3);
Resulting in A3:Q3
Hope that helps.
I have a Google Sheets file with 3 spreadsheets. Each of the first 2 contains numerous number data entries. All I want to do is change one cell value on the third (tt) if any data is changed on a different sheet. Looking at my incomplete onEdit() script, you will see I obviously don’t know what I’m doing. How to I change the value of cell ‘a5’ on third spreadsheet (tt) for any changes made anywhere on only the first two?
function onEdit(event)
{
var aa = event.SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Input-Expenses");
var ss = event.SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Input-Income-n-Assets");
var tt = event.SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Run Program");
var r = event.aa.getActiveRange('a1:r105');
var s = event.ss.getActiveRange('d1:q94');
var g = 0
tt.getRange('a5').setValue(g);
}
You need to refer to this article:
https://developers.google.com/apps-script/guides/triggers/
Find Edit trigger.
range A Range object, representing the cell or range of cells that
were edited.
Usage:
var editedRange = event.range;
var editedSheet = editedRange.getSheet();
if (editedSheet.getName() === 'Sheet1')
{
var targetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
// do smth
}
This should do what you want
function onEdit(event) {
try {
var aName = event.range.getSheet().getName(); // Active sheet
if( ( aName === "Input-Expenses" ) || ( aName === "Input-Income-n-Assets" ) ) {
// You said anywhere on these 2 sheets and put in Run Program:A5
event.source.getSheetByName("Run Program").getRange("A5").setValue(0);
}
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}
I'm trying to work out a formula to sum up values across different sheets for particular rows as in the following:
Sheet 1
A | B
---------
bob | 3
foo | 14
bar | 7
Sheet 2
A | B
---------
bob | 2
foo | 1
bar | 9
But with the caveat that a new 'Sheet 3' can be added with relevant data and will automatically be picked up.
How do I go about getting the summed values for each row in each sheet while handling new (properly named) sheets?
Results
-------
bob | 5
foo | 15
bar | 16
We can assume that the row values are all the same, such as in a named range, People = {bob, foo, bar}.
My attempt has been something like:
={People,ARRAYFORMULA('Sheet 1'!B1:B3+'Sheet 2'!B1:B3)}
but for new sheets, I would have to manually update the formula by adding in
+'Sheet 3'!B1:B3
I've tried using INDIRECT to dynamically generate the sheet names but it doesn't take an array. I'm guessing that I might have to use
SpreadsheetApp.getActiveSpreadsheet().getSheets()
in a script, but if I could do it just as a formula, it would be easier to explain to people inheriting the spreadsheet.
Thanks
I have alternate solution to your problem, using a slightly different approach.
I would suggest pulling all of the data into one results page and summing it there. Then you don't need a massive 'sum' function or a script.
Using indirect you can pull information from each sheet, provided the data is in the same cell location on each sheet. If that's not possible you could also use vlookup to pull the data. I've shared an example of how I would do this using your numbers.
Syntax for 'bob' value on Sheet1 =iferror(indirect("'"&C$2&"'!"&"b3"),"")
where C$2 is the Sheet name (in this case Sheet1) and B3 is the value for bob on Sheet1. Then you can copy this formula across the columns and update your sheet names at the top of the table.
https://docs.google.com/spreadsheets/d/15pB5CclseUetl5zSRPDOR9YA4u6_7TK8RB8CpSxqhnk/edit?usp=sharing
Sample File
The workaround is to use a custom function:
Warning! The function won't refresh automatically. It will refresh if you add and then delete row above it.
Syntax:
=sumBySheets("Sheet1", "Sheet2", "B1:B3")
"Sheet1" — sheet from
"Sheet2" — sheet to
"B1:B3" — range address for a sum.
There may be more sheets between Sheet1 and Sheet2:
The code:
function test_sumBySheets()
{
var sheet1 = 'Sheet1';
var sheet2 = 'Sheet2';
var range = 'b1:b3';
Logger.log(sumBySheets(sheet1, sheet2, range));
}
function sumBySheets(sheet1, sheet2, address)
{
var file = SpreadsheetApp.getActive();
var s1 = file.getSheetByName(sheet1);
var s2 = file.getSheetByName(sheet2);
var i1 = s1.getIndex() - 1; //get sheet indexes
var i2 = s2.getIndex() - 1;
var sheets = file.getSheets();
var result = [];
var arrays = [];
// remember all the data
for (var i = i1; i <=i2; i++)
{
var s = sheets[i];
var range = s.getRange(address);
arrays.push(range.getValues());
}
return sumByArrays_(arrays);
}
function sumByArrays_(arrays)
{
// take array #1
var arr = arrays[0];
l = arr.length;
ll = arr[0].length
// plus all arrays
for (var x = 1, xx = arrays.length; x < xx; x++) // loop arrays
{
for (var i = 0; i < l; i++) { // loop rows
for(var ii = 0; ii < ll; ii++) { // loop cells
arr[i][ii] += arrays[x][i][ii];
}
}
}
return arr;
}
Note:
please run the function test_sumBySheets first and get the permissions.
Been a while, but here's what I ultimately ended up with for my solution.
The following code dynamically loads all sheets associated with the spreadsheet (note that I edited it a bit for readability on SO, might be some typos):
// These vars indicate what sheet, column and rows to start
// the list of sheets.
var main_sheet = 'Sheet1';
var sheet_col = 'A';
var sheet_row_start = 1;
function onEdit(e) {
// The onEdit trigger is the most useful as it fires most often.
// Therefore it is more likely to update the list of sheets relatively
// quickly.
_set_sheet_cells();
}
function _clear_sheet_cells(num_sheets, sheets_length) {
// Clear sheet cells, in case number of sheets has dropped.
var sheet_ctr = Number(num_sheets);
var stats = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(main_sheet);
if (sheet_ctr) {
if (sheet_ctr >= sheets_length) {
for (var i=0 ; i<sheet_ctr ; i++) {
stats_sheet.getRange(sheet_col+(sheet_row_start+i)).clearContent();
}
}
}
}
function _get_sheets() {
// Gather our sheets, can be combined with a regex to prune sheets.
var out = new Array();
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var i=0 ; i<sheets.length ; i++) {
out.push( sheets[i].getName() );
}
}
return out
}
function _set_sheet_cells() {
var userProperties = PropertiesService.getUserProperties();
var num_sheets = Number(userProperties.getProperty('sheet_names'));
var sheets = _get_sheets();
if (num_sheets == sheets.length) {
// Do nothing, no changes, remove if concerned about renaming sheets.
return;
}
_clear_sheet_cells(num_sheets, sheets.length);
var stats = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(main_sheet);
for (var j=0 ; j<sheets.length ; j++) {
// Put one sheet name per row.
stats_sheet.getRange(sheet_col+(sheet_row_start+j)).setValue(sheets[j]);
}
userProperties.setProperty('num_sheets', sheets.length)
}
Once I had all the sheets loading dynamically, I just used #cgm990's answer, with the benefit that if I added another sheet to the spreadsheet, it automatically updated all my sums.
I’m writing a custom function in Google Apps Script that, if a certain other cell contains a number, uses the value of that cell and several other cells to calculate a result. Otherwise, if that certain other cell does not contain a number, the function should just return an empty string so that the active cell appears blank.
I was able to come up with a working function to do this, but in order to protect sensitive information, I’m not going to copy it here. Instead, here’s an example function that accomplishes the same thing:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rowNum = sheet.getActiveCell().getRow();
var rowVals = sheet.getRange(rowNum, 1, 1, 15).getValues();
var fVal = rowVals[0][5];
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
var aVal = rowVals[0][0];
var bVal = rowVals[0][1];
var cVal = rowVals[0][2];
var gVal = rowVals[0][6];
return ((gVal * fVal) + aVal + bVal + cVal);
}
However, in an effort to speed it up (and also some other reasons that would be complicated to try to explain here, so you'll have to just trust me), I want to have the custom function set the value of the cell to be a formula instead of doing the calculating itself. It doesn’t work to just put the formula in the cell in the first place because then it still calculates/shows a result even if column F doesn’t contain a number.
Here’s what I’ve tried so far:
function myFunction2() {
var sheet = SpreadsheetApp.getActiveSheet();
var rowNum = sheet.getActiveCell().getRow();
var fVal = sheet.getRange(rowNum, 6).getValue();
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
var formula = '=SUM((G2*F2)+A2+B2+C2)';
return formula;
}
^This just makes the cell display the string “=SUM((G2*F2)+A2+B2+C2)”.
So I then tried using setFormula on the active cell:
function myFunction3() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var rowNum = cell.getRow();
var fVal = sheet.getRange(rowNum, 6).getValue();
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
cell.setFormula('=SUM((G2*F2)+A2+B2+C2)');
}
^which, when I called the function in a cell, returned an error saying “You do not have permission to call setFormula”. The same thing happened when I tried getting the a1 notation of the active cell and then using getRange(a1Notation).setFormula('=SUM((G2*F2)+A2+B2+C2)') instead of calling setFormula directly on the active cell.
Anybody know if there's a way around that permission error? or have any other ideas for how to accomplish this?
The permission error is because of restrictions on what user defined functions can do. You can, however, do this with onEdit like this:
function onEdit(e) {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet();
var col = e.range.getColumn();
var rowNum = e.range.getRow();
if(col==6){
var fVal = s.getRange(rowNum,col,1, 1).getValue()
}
if (fVal == "" || isNaN(fVal)) {
return
}
else{
s.getRange(rowNum,col,1, 1).setFormula('=SUM((G2*F2)+A2+B2+C2)');
}}
I actually ended up figuring out a way to accomplish what I wanted using the built-in IF function, so that's what I did.
I'm a new to Script editor on google spreadsheet and not much of a programmer.
I've been working with google docs for sometime now and it has become an important tool in my daily activity.
What I'm trying to do, is the following:
Seek a whole document (with severall sheet such as "1", "2", "3", and so on, corresponding to the number of days a month can hold) and if the column 7 shows a determined value (in my case, it will be RECEBER), pull all the data in that row and write onto a sheet created for this purpose.
What's happening is that I'm using the the event onEdit to trigger this function. At first sight, it would be ideal, but in my case, I copy a lot of data from other spreadsheets and the paste command does not trigger the onEdit event. Instead, I have to edit the cell manually in order to get that row copied onto the other sheet.
I could run it just once, once tha whole days of the month were filled and there were any changes left to do, but what I really want to do is to make it immediately, as soon as the content is inserted into the spreadsheet.
There's also another problem with my code, it has to be fit and adapted to all the other sheets, as the if clause only performs the full operation if the active sheet equals "1". Anyway, I believe there is a simple solution to this.
Here's the code found on the net that already took me halfway:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "1" && r.getColumn() == 7 && r.getValue() == "RECEBER") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("money");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
}
I'll appreciate all the help you can give.
Thanks in advance.
Diogo Sousa
-- Updated 12Oct --
I've changed the logics on my code, and as patt0 suggested, I run the script from the menu created. I've tried adapting the code, but I believe there's some section wrong. The script runs, but isn't writing anything at all on my target sheet.
here's the code:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [ {name: "RECEBER", functionName: "RECEBER"} ];
ss.addMenu("Scripts", menuEntries);
}
function mustBeCopied(sheetName) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNumber = parseInt(sheetName);
if (sheetNumber <=31 && sheetNumber >=1)
return true;
return false;
}
function RECEBER() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var r = ss.getActiveRange();
if(mustBeCopied(s.getName()) && r.getColumn() == 7 && r.getValue() == "RECEBER") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("money");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
}
the function Mustbecopied, from what I believe, only sets the range of sheets (1 to 31) eligible;
the function RECEBER will determine if the value on the column 7 will satisfy the condition (RECEBER), so it can retrieve all the row information and move it to the target sheet.
Maybe the trouble is the active sheet thing.. can I us eit in my own advantage and apply the script to the selected sheet?
Also, if I could have both option (whether to apply it to the selected sheet or to the whole document), that wouuld be great and simplify my daily work a lot!
Your question is really multiple questions in one, and I will only deal with the second part, the first part has quite a number of dimensions (how to update summary sheets effectively), which we can discuss with another question maybe.
In order to determine if the data in a particular sheet needs to be copied, you could create a simple function that returns a boolean if the sheet satisfies the condition that it is a number between 1 and 31.
function mustBeCopied(sheetName) {
var sheetNumber = parseInt(sheetName);
if (sheetNumber <=31 && sheetNumber >=1)
return true;
return false;
}
Bear in mind that a sheet with the name "28.7" would satisfy the condition.
The first line of your onEdit() script would then look like this
if(mustBeCopied(s.getName()) && r.getColumn() == 7 && r.getValue() == "RECEBER")
Let me know if this works for you.
--- Oct 14 ---
As far as I can see, the issue that remains, is that when your function was running onEdit, the range (active range) was the cell that was just edited. So the range would be only one cell.
In order for the scrip to run, you would need to highlight each cell to be copied before selecting the menu, which would be tedious to say the least
Further if you have a defined range to be copied, which is the same on each page, we can copy that range every time, or iterate through each row in the column to look for "RECEBER" and get those rows copied.
Further We can try to update your function so that iterate through all the sheets is a many similar to this.
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var i = 0; i < sheets.length; i::) {
var s = sheets[i];
if (mustBeCopied(s.getName()) {
var range = s.getRange(someRow, 7, someNumOfRows);
for (var j = 1; j <= someNumOfRows; j++) {
if (range.getCell(j,1).getValue() == "RECEBER") {
//YOUR COPY CODE
}
}
}
}
Let me know how this works.