This is important because the order of execution is based on the first created sheet to the last created sheet which ordinarily is not an issue but is an issue for circular references. Obviously I can put something in the name to remind me but there seems to be no pattern to the gid number and therefore have no way to know for already created spreadsheets with custom sheet names without painfully testing circular reference formulas.
This is not part of my question but here is an example to demonstrate why this is so important:
Step 1) File->Spreadsheet Settings->Calculation->Iterative Calculation(On)->Max number of iterations(1)
Step 2) Set up the following test formulas
Cell A1 =A1+1
Cell B1 =A1
Cell A2 =B2
Cell B2 = B2+1
You will notice that even though they formulas should have the same results they do not due to the execution order, now this example is just of a single sheet but invisibly the original sheet creation order is the order of execution when formulas reference other sheets.
The fastest solution that satisfies your request is to use the getSheets method to obtain an array where every sheet appears in the same order that Sheet shows in the user interface. Keep an eye on them, because if you move one sheet around in the user interface the array won't reflect the true order of creation.
Another approach, more precise than the former one, is to use the properties class to save a timestamp every time that a sheet is created. You can manage that with the setProperty and getProperties methods.
Related
PROBLEM: In the below spreadsheet, cells C12:C17 (green) contain text imported from another spreadsheet (The "PARENT").
https://docs.google.com/spreadsheets/d/1brm0dHkXG1vxn2NQ7wGvEoayCdGYKsY4yA-MX4Jtt1w/edit#gid=396314711
Some cells have text. Some are blank. Some contain simple math (i.e 1+1), and others relational math (i.e. A1+B1). The DATA SET in the PARENT sheet are different than the CHILD sheet. I will eventually have to create a lot of CHILD sheets, each with their own unique DATA SET. The formulas on the PARENT sheet will change from time to time, so the solution isn't to just make a copy of the PARENT spreadsheet and turn it into a CHILD sheet.
I need to be able to create the formulas in the PARENT sheet, but when imported into the CHILD sheet, they need to be calculated using the data set on the CHILD sheet.
I'm trying to use a script that will take what is in the C12:C17 range, and make it an active formula in the corresponding D12:D17 cells. If the C column cell is a value, it should just put that value in there instead of making it into a formula.
I've made about a dozen attempts at the setFormulas script. All failed. At this point, I would say I'm "spit-balling", "flailing", and possibly "spiraling". The BEST I've been able to do is to get ONE cell to update (but only if a formula, not text).
I'm looking for a script that will take whatever is in cells C12:C17, and execute/evaluate them into cells D12:D17. If there are text/values in the C cell, then it should put that text/value in the corresponding D cell. If there is a formula in the C cell, it should make it execute in the corresponding D cell. There is no pattern on whether a cell will be a number, text, or formula.
I appreciate any help you can give.
FYI: The formulas/text in the C column were made by importing from the PARENT sheet. That range of cells in the PARENT sheet was made by the following formula:
=IFERROR(FORMULATEXT('P CUSTOMERS'!B12),'P CUSTOMERS'!B12)
Essentially, "If it's a formula, convert it into text. If it's not a formula , just put what is in the cell in the first place ."
I've been working on this for an embarrassing amount of time... (Not hours, not days, not weeks, but MONTHS!).
EDIT / UPDATE:
OK, Marti's scripts worked great in the example file. Moved it to another file, same conditions, and worked again.
THEN, I moved it to another file... Only difference I can tell is that it is MUCH larger.
EDIT SUMMARY:
Ran the script under these conditions.
Made table with formulas. Used FORMULATEXT on that table to convert into text. Imported that range to another sheet. Used an HLOOKUP formula to select which column I wanted to look at formulas for.
In the sample sheet (linked here: https://docs.google.com/spreadsheets/d/1cPSJMXNiKDnHCiUCGJ0iSjaoIiRLQldr1T5rClUUEm0/copy), it works.
But when I run the exact same series of events in another sheet, it fails to do anything. Process is the same, only the range is different. No other scripts on this sheet.
I trimmed down the second sheet so I can share (script still doesn't work).
https://docs.google.com/spreadsheets/d/1RRMy4RtF9CVSXw18bWg79Dh4nwHg8IN3wwhLb3QynvA/edit#gid=236899042
(Note: I can't force a copy, as this sheet requires authorizations from another file)
Here is a video better explaining the issue:
https://drive.google.com/file/d/1pjz_LilRReQlNt7p_4NhU3prtbcAhLah/view?usp=sharing
So, I'm trying to understand why it works in one, but not the other... And just as important, what can I do to make it work in 2nd sheet, which is actual goal.
You can simply use getValues together with setValues. setValues actually interprets values starting with = as formulas, and getValues actually doesn't add the ' before them. So you can simply chain them:
function computeValues() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const s = ss.getSheets()[0]
const src = s.getRange('C12:C')
const target = src.offset(0, 1)
target.setValues(src.getValues())
}
References
Range.getValues() (Apps Script reference)
Range.setValues(values) (Apps Script reference)
Range.offset(rowOffset, columnOffset) (Apps Script reference)
Martà nailed it...
function computeValues() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const s = ss.getSheets()[0]
const src = s.getRange('C12:C')
const target = src.offset(0, 1)
target.setValues(src.getValues())
}
I tested it, as written, by copy/paste. Worked 1st time.
I added in more formulas below the original range, just to see what the limits were (in cell C20), and re-ran it. Still worked.
I would have kept going in my setFormula direction for another few months. Not sure how I got it stuck in my head that was the only solution. This was the final major barrier in my project, and while I still have another few years of manual data entry and formula creation to do, THIS will be the key that makes it all work. THANK YOU!!!
I have a dashboard that is reporting live data based on a growing customer database, and I am currently tracking the daily process by copying and pasting the data in the column into the next on a daily basis.
I'd like to create a script that automatically copies the values from an array (B:B) for example, into another sheet OR into the next available column to the right.
You may refer to the image below for further clarity.
Sample
I saw may threads on this but they were all about copying into a different row, as opposed to a different column.
Many thanks in advance!
The getLastRow() method is used to return the position of the last row that has content.
The getLastColumn() method is used to return the position of the last column that has content.
Therefore, in order to be able to copy the data into the next available column, I suggest you try this:
function copyColumn() {
var ss = SpreadsheetApp.openById("ID_OF_THE_SPREADSHEET");
var sheetFrom = ss.getSheetByName("SHEET_FROM");
var sheetTo = ss.getSheetByName("SHEET_TO");
var values = sheetFrom.getRange(NO_OF_THE_ROW_WHERE_DATA_STARTS, 3, sheetFrom.getLastRow(), 1).getValues();
sheetTo.getRange(NO_OF_THE_ROW_WHERE_DATA_STARTS,sheetTo.getLastColumn()+1,values.length,1).setValues(values);
}
The above script localizes all the data that needs to be copied by using the getRange() method and is copied by using getValues().
NO_OF_THE_ROW_WHERE_DATA_STARTS is the value representing the number of the row where your data starts (in your case the row of No of LP - Secured);
3 is the value representing the C column (where the data you want to be copied is located);
sheetFrom.getLastRow() is the value representing the end of the data you want to be copied;
1 is the value representing the number of columns that need to be copied.
Afterwards, the getRange() method is used again in order to be able to identify where the data needs to be pasted and setValues() in order to actually paste it.
Note: The above script works for different spreadsheets and/or different sheets OR for sheets in the same spreadsheet. If you want to use it for the latter case, you just have to put the name of your sheet instead of SHEET_FROM and SHEET_TO.
Moreover, I suggest you check the following links since they might be of help:
Sheet Class Apps Script - getRange();
Range Class Apps Script - getValues();
Range Class Apps Script - setValues();
Sheet Class Apps Script - getLastColumn();
Sheet Class Apps Script - getLastRow();
I need cell B3 to reference B1 while blank.
Once you put something in B1 it'll keep that value forever, even once B1 get's changed to something else.
This is my situation:
Basically I have a sheet that is fed by a Google form and each submission needs three key reference numbers each kept in columns a,b,c
A = Unit Number/Individuals name (There may be duplicates down the sheet as this is per submission)
B = Work Order (Imputed by me after actual work on unit has been done)
C = Cry Number/Reference number (Automatically generated per submission; no duplicates)
I then have a frozen row at the top which contains a search bar that you can search for the cry number (A1)(Which has a Data Validation set to column C so that you can only search valid cry numbers) and then a cell to add a W/O to that Cry Number (B1)
In column B3:1000, I have this formula copied down:
B3=if(isblank($C3),"",if($A$1=$C3, SUBSTITUTE($B$1,"",$B$1),""))
...which makes it so that if you select say "CN-168" (A valid cry number) and in 'B1' type "W1134" that work order number will be assigned.
Now I need that work order to stay there regardless of when 'A1' changes so that you can do the process over again on another submission.
Is it possible to do with formulas? If not, then a Google Script?
Here is a template of what I'm dealing with but not to the same scale as my Data Base
Its not possible with formulas but easily done with apps script. look at the onEdit trigger and the documentation for SpreadsheetApp to setValues to the appropiate ranges.
If you want to be 100% complete you also need a time trigger (say every 10 minutes) to check that a row wasnt missed. It can be missed during apps script errors/outages or when the sheet is changed from outside the sheets webpage/app (For example using the http spreadsheet api)
I have been struggling with the following situation for almost the whole last week and it would be awesome if someone could give me some hint.
The situation:
1. Script finds a particular value in, lets say, 'Sheet1', and gets the row number of the cell containing this value.
2. Since script has found this value, it executes a bunch of actions like creating new spreadsheet and copying numerous 'Sheet1' from dozen other spreadsheets to this newly created spreadsheet, and comparing/ analyzing data.
The problem.
- While script is doing a bunch of other actions, user is able to rearrange cells by, for example, sorting A to Z, which changes the address of previously found cell.
Here is the code that I used in order to verify this:
function WhatHappensIfUserSorts () {
var ss = SpreadsheetApp.getActive();
var sh = ss.getActiveSheet();
var rng = sh.getRange("B17"); //Lets say that script finds this cell according to some rules
Utilities.sleep(10000);
rng.setValue("Test Value");
}
Question:
Might there be any workaround for this?
My current ideas.
1. I was thinking about hiding the filter row in the beginning of the script, but this doesn't help a lot, because users can insert new row in the which will change the addresses of the rows below.
The background.
I am trying to create two way synchronization, meaning, each project member has his/ her own spreadsheet with 'Project X', 'Project Y' etc. sheets and no matter who updates their project sheets, all other users that work on the same project get these updates in their project sheets. These updates that have to be tracked are not just the cell values, these are cell notes as well. And this is the reason why script has to do the bunch of other actions, since CopyTo method does not work between spreadsheets.
During my research I found sheetSpider project, but it seems somewhat different and too complicated from what I need.
A simple suggestion would be to give each row a unique identifier so that you could use it to evaluate the target range again before you write back to the sheet.
get target row's unique ID --> do work --> locate target rows ID and use to determine write range --> write back to sheet.
Alternatively, during the operation you could delete the target row and then use appendRow() to drop the updated version back in.
A third and final suggestion might be to temporarily suspend the permissions for the sheet. See: https://developers.google.com/apps-script/reference/spreadsheet/page-protection#setProtected
I am trying to write a GAS spreadsheet custom function that copies cell content to other cells. And I need to fill the target cells not only with the data of the source cell, but with its formula content (if it has any).
Now, I already know that this is basically impossible through custom functions as they always receive the result of cell calculations but not the cell formulas themselves, and they also cannot return formulas for their target cells.
On the other hand there are functions to read and write cell formulas, e.g. Range.getFormula() and Range.setFormula() which seem to make my endeavor possible. I simply have to find another way of calling them. UPDATE: Meanwhile I found that custom formulas in fact can read formulas using getFormula(), but they definitely don't have permission to write formulas into cells using setFormula().
My question is...
What would be the most elegant method to create something equivalent to a custom function that reads and writes formula content of cells? I think I could use an onEdit function that updates my target cells after each spreadsheet edit, but that would mean that I have to hard code the coordinates of the target cell range, which seems very hacky and would require code changes every time the target range is moved (e.g. when rows are inserted above it).
UPDATE: Example
An example would be a custom function that is able to read multiple ranges of cells (each range given as a distinct function parameter) and returns a joined range of cells.
=rangeJoin(A1:B10;D1:E15)
...would read the two ranges of size 2x10 and 2x15 and would fill a target range of size 2x25 with the subsequent cell contents of both ranges. The target range would start at the cell that contains rangeJoin and would spread 2 cells to the right and 25 cells down (as usual for a custom function). The custom function (or similar mechanism) should be able to copy formulas, so a cell containing =hyperlink("http://www.google.com";"Google") should appear in the target range as a hyperlink and not as a text cell with the naked word 'Google'.
Agree with "Mogsdad"
ie. this custom function works:
function myGrid() {
return [[1,2],[3,"http://www.google.com"]];
}
but, custom functions can't write formulas to a sheet. See https://developers.google.com/apps-script/execution_custom_functions#permissions
As a workaround, you could use a "Trigger", such as a time based trigger, as "Mogsdad" suggests.