I have got a dictionary from an URLfetch. What I want to do is write the values in the row the script is launched from, but I don't know how to navigate trough the cells.
So how to get the range of the cell a function is launched from?
Thanks
function separate(Json) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("JsonData");
// cell = range of the cell the function is launched from
sheet.getRange(x).setValue(0."currentvalue")
// where x is the value on the b columns of the same row
sheet.getRange(y).setValue(0."marketcap")
// where y is the value on the c columns of the same row
// repeating this for every value of the dictionary that I want in my sheet.
return Json.O."id" // write the id in the cell in which the function is launched from
}
Here is how I want to call the function
Dictionary in a list taken into argument as Json
I assume that you have the data being written vertically right now. If your goal is to write it horizontally, then you can use TRANSPOSE() around your function to transmute your column into a row-
UPDATE
The solution above will work in a formula based approach. If you want to use an Apps Script approach, you could do the following. First you have to familiarize yourself with Range.getValues() and Range.setValues(). The first method will read values from a given range. In this scenario it should be used to load the dictionary values into Apps Script and save them into an array. At this point you have an array with the dictionary values stored in a column-life fashion, but since you want them in a row-like fashion you should transpose the array. To do it easily you can use the function at the bottom. Now you can safely take your row-like array and paste it into the Sheet with Range.setValues(). Please write back with any doubt about this second answer.
function transposeArray(array) {
return array[0].map((col, i) => array.map(row => row[i]));
}
Related
I have little to no coding knowledge, so apologies if the solution is too obvious!
I am trying to add a Last Modified column to a Google Sheets file. To do this, I am using an AppScript function with the following code:
function setTimestamp(x) {
if(x != ""){
return new Date();
}
}
This works fine when I use setTimestamp(x) in my file. However, I am combining this with a Zapier action that creates a new row whenever new media is added. Every time a new row is created, any existing formulas are removed.
I assume I need to use ARRAYFORMULA to apply the setTimestamp formula to newly-created rows, but it must only apply to rows that aren't blank.
I have tried the following:
={"Last Modified";ARRAYFORMULA(setTimestamp(A2:A))} -> Only worked on first row
={"Last Modified";ARRAYFORMULA(B2:B=setTimestamp(A2:A))} -> Broke the file
={"Last Modified";ARRAYFORMULA(IF(A2:A)=1,setTimestamp(A2:A),"")} -> Expected 1 argument, got 3
Is there a way I can combine the IF into the script or a better way to solve the problem?
A public version of my file is available here: https://docs.google.com/spreadsheets/d/13zkVRPr2Wh5bHjCT8cenInHnBk7qkMkuEMdwUxC_cRU/edit?usp=sharing
All data is dummy data and stock photos.
Unfortunately, arrayformula does not function as an array map function for custom functions. (Even for native functions where you may expect it to work that way, it does not always, sadly.)
To handle array range, we need the custom function to handle array range directly. That also limits the number of individual calls to custom functions, which materially saves execution time.
To handle array range, there are 2 ways. I'll comment on both.
Array range directly as input of custom function
If the input is a single cell, it is read directly
If the input range spans more than a single cell, the data is read as nested lists: a list of lists of rows.
For example, A1 will be read as the data in A1. A1:B2 will be read as [[A1, B1], [A2, B2]].
You can remember it as columns of rows.
As for the input data format, numbers are taken without the display format. Texts are taken as strings.
If output is an array range, the result will automatically expend.
Thus, in your example, in B2 you can almost do
=setTimestamp(A2:A)
where setTimestamp() has been modified to
function out = setTimestamp(arr) {
out=Array(mat.length);
for (i=0;i<mat.length;i++){
j=0
if(arr[i][j] != ""){
out[i]=new Date();
}
}
return out
}
For more details, see the official help page. (Over the years, more details have become available.)
Almost, but not quite. For your direct question, above provides the answer. However, you seem to have an implicit requirement that your custom function is executed every time a new URL is found. Be careful that what happens here is that every time Google Sheet updates cell content, a new Date() is created and outputted.
Array range read within custom function
Since you know your URLs are in A2:A, and you want the output of your custom function to be B2:B, you can read and modify those ranges directly within your custom function via the Range Class.
In this route, you may find getLastRow(), getLastColumn() in Sheet and getNextDataCell() in Range convenient.
When you need to execute your custom function, you can run it manually or add onEdit() trigger to your custom function. (But onEdit() itself can mean substantial UI lag when using the sheet. It's usually more appropriate for sheets that parse external data automatically. See other triggers in the link for motions.)
In your example, you can almost do
function setTimestamp() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow();
var row=1;
var cell = sheet.getRange(row,1).getValue();
while (row<=lastRow) {
if(cell.getValue() != ""){
sheet.getRange(row,2).setValue(new Date());
}
cell = sheet.getRange(row,1,lastRow).getNextDataCell(SpreadsheetApp.Direction.DOWN);
row=cell.getRow();
}
}
which will scan for all URLs in A2:A and write current time to B2:B when executed.
Again, your example implicitly points to updating only when a new URL is found. So be careful about that. Use triggers as needed.
As for the need to place formula in B1, you can (and should) reference the output of your other application in a different sheet so that you or a different application of yours can edit without conflict.
Thus, for what was asked, we have everything.
I have more than 20k form responses (google sheet and google form) where some guys have selected the wrong data which is visible in my responses. How I know its wrong is because they needed to select the activity (an attribute) but they selected the similar activity name (let's call it X) which was for the previous year and this year's activity (let's call it Y) should have been the different one.
I know that after a certain date all the X activities are Y, so I need to modify the data while importing it from the responses.
I tried conditional formatting on the data but then the importrange doesn't work, it needs cells to be empty to work.
I learned about query statements but it doesn't allow UPDATE.
Please help me do this, I am okay if we need to use a macro. I'm looking for something like this (note that the following is the logic I'm looking for and not the actual code):
if date>"a date" and FC==X:
FC=Y
#FC being the column I wanna modify
Edit: I am unable to share the table as its confidential. Can tell you that first column is date/time of form and then there are 149 columns, one of them I need to modify based on the date. Let's Assume it has just 2 columns, A: date, B: activity (has 20 activities). So, if they have filled "X" activity after then change that activity to Y. I hope it helps in understanding.
Edit 2: Have put a dummy file as asked. So now the problem statement is after 21 May 2022 (inclusive) all "6" activity must be "2"
Try
function onOpen() {
SpreadsheetApp.getUi().createMenu('⇩ M E N U ⇩')
.addItem('👉 Update', 'myFunction')
.addToUi();
}
function myFunction() {
// parameters
var param = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Param')
var from = param.getRange('A2').getValue()
var before = param.getRange('B2').getValue()
var after = param.getRange('C2').getValue()
// data
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var range = sh.getDataRange()
var data = range.getValues()
data.filter(r => (r[0] >= from && r[1] == before)).forEach(r => r[1] = after)
range.setValues(data)
}
To avoid hardcoding and potential issue with dates, I put all parameters in a new tab called "Param" as follows
I add script using tools/script editor. I add my simple popup function :
function test(){
Browser.msgBox('This is test')
}
Then in google sheet, i add an image as button and attach the script 'test'.
It works fine, but i want to pass value from a cell. How can i do it?
For example, i modify the function as follow :
function test(val){
Browser.msgBox('This is test. Value is '+val)
}
if i want to pass value from cell B4 --> test(B4). How to do it properly ? i'm not allowed to include the parameter B4 when assigning script to image/button. It will say 'script could not be found'.
Thanks
JohnA's solution is correct, but I want to add something to it. I recommend this enhancement to avoid modifying your script every now and then when you want pass another cell as a parameter.
Approach:
Store the cell notation you want to pass on a specific cell. (e.g. "B4" in A1)
Get the value of A1 and access the value of that cell notation.
Code:
function test() {
sheet = SpreadsheetApp.getActiveSheet();
// cell holds the range you want printed
cell = sheet.getRange("A1").getValue();
// value now holds the value of the range we got from the cell
value = sheet.getRange(cell).getValue();
Browser.msgBox(value);
}
Sheet:
B4:
B5:
Note:
Everytime you change the value of the A1, it will print that cell's value.
It will not behave properly when you write an invalid range in A1. (Exception: Range not found)
Correct, you can specify a parameter if calling the custom function as a formula in the sheet, but not as attached to a script when a button is pressed. We do not even included the parentheses when we assign a script.
If the customer function always wants to use the value of B4, you can modify your script the get the value from that cell.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var val = sheet.getRange("B4");
What I am trying to accomplish is I would like to search for a term in one cell, if that cell has the term write text to another cell. My specific example would be I would like to search for the term 'DSF' in column 4. If I find 'DSF' it would then write 'w' in column 5 & write '1.2' in column 3. This is searched per row.
I do understand the the .setvalue will write the needed text, but I do not understand how to create a search function. Some help would be greatly appreciated.
EDIT
Here is the code I am working with at the moment. I am modifying it from something I found.
function Recalls()
{
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues(); // read all data in the sheet
for(n=0;n<data.length;++n){ // iterate row by row and examine data in column D
if(data[n][3].toString().match('dsf')=='dsf'){ data[n][4] = 'w'}{ data[n][2] = '1.2'};// if column D contains 'dsf' then set value in index [4](E)[2](C)
}
//Logger.log(data)
//sh.getRange(1,1,data.length,data[3].length).setValues(data); // write back to the sheet
}
With the Logger.log(data) not using the // It works properly but it overwrites the sheet, which will not work since I have formulas placed in a lot of the cells. Also, Maybe I did not realize this but Is there a way to do a live update, as in once I enter text into a cell it will research the sheet? Otherwise having to 'run' the macro with not save me much time in the long run.
Try this. It runs when the sheet is edited. It only captures columns C,D,&E into the array and only writes back those columns. That should solve overwriting your formulas. It looks for 'DSF' or 'dsf' in column D (or contains dsf with other text in the same cell either case). Give it a try and let me know if I didn't understand your issue.
function onEdit(){
var sh = SpreadsheetApp.getActiveSheet();
var lr = sh.getLastRow()// get the last row number with data
var data = sh.getRange(2,3,lr,3).getValues(); // get only columns C.D,& E. Starting at row 2 thur the last row
//var data = sh.getDataRange().getValues();// read all data in the sheet
for(n=0;n<data.length-1;++n){ // iterate row by row and examine data in column D
// if(data[n][0].toString().match('dsf')=='dsf'){
if(data[n][1].match(/dfs/i)){ //changed to find either upper or lower case dfs or with other text in string.
data[n][2] = 'w';
data[n][0] = '1.2'};
}
sh.getRange(2,3,data.length,data[3].length).setValues(data); // write back to the sheet only Col C,D,& E
}
I have some trouble with a spreadsheet: as said in the title, I put in a column a formula which is calling a custom script based on the value of another cell, but when I modify this other cell, the formula does not update... This seems to work with standard formulas, but, in my cell calling my script:
If I try to add a blank in the fomula cell, the result is still not updated.
If I clear the formula cell, and re-type the formula, it's still showing the old value.
If I copy paste the formula cell in another one, the new cell is up-to-date.
Here is my script. If few words: for given 'company' parameter, it search for all rows matching this criterion and store the 3rd column cell in a variable, finally returned ( so return the last value ):
function getLastStatut(company) {
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var out = "not found";
var row;
for(i in values){
row = values[i];
if(row[1]==company){
out = row[2];
}
}
return out;
}
And for example:
A1 : Date
A2 : Test
A3 : Running
A4 : =getLastStatut(B1)
So A4 display "Running", but if I change A3, it still shows "Running", whereas it should display the value of A3.
Is this a bug or is there something I'm doing wrong? Any help is welcome.
Alexis
The problem is with the caching "feature" for custom functions. I explain it in this other thread, please read.
But the bottom line here is, a custom function should not access data that is not static, all variable information should be passed as parameter. In your case, the whole data set should be a parameter of your script. Custom functions should never get any range at all.
//getting values like this is wrong (for a custom function)
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues();