For our construction company, we have a sheet that has all of the bills listed, along with relevant data that our accounting person would add to the master sheet.
I then have another sheet that pulls this data for the relevant people in the accounts for them to complete those steps. It filters to only the relevant columns (specifically, based on Column H - either "Yes" or "No") using query and importRange.
query(IMPORTRANGE("https://docs.google.com/spreadsheets/d/1pY53-XaGnUQ3BPmLh90mLSqIwSo7S2_QOPbD6JBQHOA/edit#gid=0","Master!A3:G"), "Select Col1, Col2, Col4, Col5, Col6, Col7 where Col6 is not null")
I want to include a few details in the destination sheet, which I have done.
The problem is typically associated with column H in the master sheet (Work Done required or not). For most cases, it is either a yes or no. However, in some cases, the accounting person doesn't know for sure whether it is a yes or no. But he wants to keep on adding other bill details.
When he fills the empty column later, the entered data on this second sheet doesn't dynamically shift with the imported data, thus causing the rows to misalign.
Unfortunately, as I mentioned, the rows don't stick together so as the dynamic order of the imported columns changes, the static order of the manual columns causes a mismatch.
Is there a way to make this work?
A solution would be to add an onEdit trigger in order to check if any changes have been made to the H column and later add the corresponding rows to the destination sheet, something similar to this:
function onEdit(e) {
let destinationSheet = SpreadsheetApp.openById("SS_ID").getSheetByName('Input Sheet');
let sourceSheet = SpreadsheetApp.getActiveSheet();
let lastRow = destinationSheet.getLastRow();
if (e.range.getColumn() == 8 && e.range.getValue() != null) {
let billNo = sourceSheet.getRange(e.range.getRow(),1).getValue();
// retrieve all the other values needed from the current row of the edit
// current row of the edit > e.range.getRow()
destinationSheet.getRange(lastRow+1,1).setValue(billNo);
// copying the values to the destination sheet by using setValue()
}
}
The above code makes use of the e event object in order to check if the edit has been made in the H column and if the value is different from null. If this condition checks, the values needed to be copied are being retrieved (here I have illustrated how to retrieve the billNo) and later set them in the destination sheet by retrieving the last row of it.
Note
You can also add a for loop in order to manage the copying the data easier and also add more conditions such that it the new entry is being pasted after a certain Actual Date or simply sort the values after the entry is being pasted.
Reference
Apps Script Triggers;
Apps Script Event Objects;
Apps Script Range Class.
I have performed testing with the following formula, I found no issue even I remove certain value for certain cell in "Col H", and the result is still correct, am I miss out certain information? I just change the filter criteria based on Col H only
=query(IMPORTRANGE("https://docs.google.com/spreadsheets/d/1pY53-XaGnUQ3BPmLh90mLSqIwSo7S2_QOPbD6JBQHOA/edit#gid=0","Master!A3:H"),"Select Col1, Col2, Col4, Col5, Col6, Col7 where Col8 is not null")
Related
I am very new to appscript and any help on the below query will really be helpful.
I am having data in Sheet1 in column A and Column B, now I want to design an appscript which get the below job done.
(1) Sheet1 Column A has Roll number along with student name i.e. Roll No.*Name, column B has actual value (these both columns are user input).
(2) Sheet2 has two columns Roll No. and Dummy value.
(3) In Sheet1 column C, I want the dummy value from Sheet2 column B by matching the Roll number of both sheets, however, I want value only in those rows in which actual value (column B) in Sheet1 is blank.
For reference I am sharing the link of the sheets.
https://docs.google.com/spreadsheets/d/1vI22QCmixKe3aoWMLODTFzt7pNXIKO3pjXS4mT6GHT0/edit#gid=524973836
I had checked different question on this community but not found a solution to my query.
Any help on above will really be appreciated.
Regards
Assume you want to put down 'dummy values' in Sheet1 column C if column B is empty, and that so call 'dummy value' need to has its 'Roll No.' value matched with the number of the front part of 'Roll No.#Name' column in sheet1:
Solution - 1. in-app lookup function:
Put this code into Column C2 in sheet1.
This code lookup for matches of 'Roll No.' from sheet2 column A whenever there Column A is not empty and Column B is empty in sheet1.
=ArrayFormula(
IFS(
$A$2:$A="","",
$B$2:$B<>"","",
TRUE,XLOOKUP(INDEX(SPLIT($A$2:$A,"*"),,1),sheet2!$A:$A,sheet2!$B:$B)
)
)
Solution - 2. write a custom function in apps-script:
in-app call:
apps-script js code:
this solution create a custom function called 'MYLOOKUP()' with apps-script, and call the function form the spreadsheet.
// apps-script:
function MYLOOKUP(data1,data2) {
return data1
.map(([rollNo_Name,value]) => {
return (rollNo_Name !== '' && value === '') ?
data2.find(([rollNo,]) => rollNo_Name.split('*')[0] == rollNo)[1] :
''
});
}
in-app call:
=MYLOOKUP(A2:B,sheet2!A:B)
There are a lot other solution to your question if you mention apps-script, such as...
instead of calling the custom function in-app, you can instead create a custom menu to run the function when you click on it,
or add simple trigger so that every time the spreadsheet is edited, the function will be run one time.
you can even create a web ui for this thing to run, etc.
On Google form submission, I am trying to get a unique patient id in column A automatically whenever there is a response being submitted. You can also see the formula in the formula bar.
As of now, I am able to do it with the following google script but due to the trigger limit, it is not getting fulfilled as the COVID response is quite higher and requests are keep coming in.
function myFunction1() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var lr=sheet.getLastRow()
sheet.getRange(lr,1).setFormula('="PID"&row()-1');
}
If I add the formula to all cells in column A of this sheet, every time a new row is being inserted on form submission and the formula is NOT available for that row by default.
So, Is there a way that I can automate generating the PID automatically.? or any other smart ideas without the trigger option?
Alternative ARRAYFORMULA in cell A1:
=arrayformula({"Patient Unique ID #";if(B2:B<>"","PID"&row(A2:A)-1,iferror(1/0))})
"Patient Unique ID #" puts the title (Patient Unique ID #) in cell A1, then the ; returns the line.
If B is not empty then generate the unique ID as per "PID" and a unique number based on ROW() less 1. If B is empty, then nothing iferror(1/0).
In Google Sheets, this sort of thing is usually done with an array formula. I would recommend that you leave the 'Form Responses 1' sheet as is and insert the timestamps in a new sheet with something like this:
cell A1:
=arrayformula(
ifs(
row('Form Responses 1'!A1:A)) = 1, "Patient ID",
isnumber('Form Responses 1'!A1:A), row('Form Responses 1'!A1:A) - 1,
true, iferror(1/0)
)
)
cell B1:
={ 'Form Responses 1'!A1:Z }
If you absolutely want to insert the Patient ID column directly in the form responses sheet, you may want to place it on the right after the columns that get updated by the form.
An array formula is more reliable than using a function that runs on a trigger, but do note that the patient IDs will be dynamic and only remain valid as long as no rows are inserted or deleted in the data. The same is true with your current approach.
To get static IDs, use an on form submit trigger that writes an ID on a row when that row is first submitted. See the insertUniqueId_ script for one example — it runs on an on edit trigger, but can be modified to run on an on form submit trigger.
consider this situation.
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("2:2");
var values = range.getValues();
values[0][0] = "updated value"; //updating retrieved data
sheet.insertRowAfter(1); //here the problem appears
range.setValues(values); //trying to save updated row data - FAIL!
Updated row values will be written in a row number 2 (as per range selector "2:2") not in new position "3:3".
Result: It will overwrite data at row 2 instead of updating previously selected/loaded row, which is now at position "3:3" and not anymore at "2:2".
Im looking for a way how to keep "address" of a range even when the range was shifted because of added/removed rows/cells somewhere else in the sheet.
In this dummy example I can track range changes. But in parallel processing/updating sheet i can't simply track all changes from many different places.
So far I came up with a solution add new rows only at the end of SS (that doesn't change range of any rows above, but I would like to have ability add new rows on top of sheet without influencing already selected ranges.
Deleting rows is also dangerous situation - it changes position of ranges also.
LockService can't really solve the situation, because I have plenty scripts working on the same sheet (not using centralized library because of speed performance in addon).
Metadata for cell or row seems too complicated to handle for such an easy task.
From my point of view Object Range should keep its position even when its moved/shifted somewhere else. Otherwise I can't see reason to have Range as an Object - if it keeps only fixed information about from where it was picked.
Any advices are welcomed. Thank you in advance..
EDIT:
Just to add a context. I'm using Google sheets as a database for orders (10 thousands so far) - each row means one order (customer) and not all orders are in one sheet - different products have different sheets (+- 10 products/sheets)
There is an suggestion using named ranges to solve this issue - So what will happen if a spreadsheet will have ten of thousands named ranges - can that work without serious performance issues? Im thinking about to creating named range for each order row, so I can easily pick up right row by orderId and not to be afraid of moving a row when new order arrive during processing another one
You want to achieve the following flow.
Retrieve the value from "2:2" (row 2).
Modify the retrieved value.
Insert a row just below of the row 1.
By this, the initial row 2 is moved to the row 3.
Put the retrieved value to the row 3 which is the initial row 2.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several answers.
Issue and solution:
In your script, range of var range = sheet.getRange("2:2"); is the constant. By this, even when the row is inserted, this value is not changed. This is the reason of your issue.
I think that the idea using the named range will be resolved for your situation. But your comment says as follows.
I just found, that namedRange is not capable actualize its own address, if something had changed in the sheet after initialize of a range.
When the row 2 is installed as the named range, when a row is inserted to just below of the row 1, the range of the named range is also changed. But from your comment, I thought that the named range might have not been reloaded.
In order to confirm this, I would like to propose a sample script.
Sample script:
Before you run the script, please install a named range to row 2 as the name of sampleNamedRange. Then, run this script.
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Added
var sheet = ss.getActiveSheet(); // Modified
// Retrieve the range from the namedRange.
// namedRange of "sampleNamedRange" is installed for the range of "2:2" of the active sheet.
var range = ss.getRangeByName("sampleNamedRange"); // Modified
var values = range.getValues();
values[0][0] = "updated value"; //updating retrieved data
sheet.insertRowAfter(1);
// Retrieve the range from the updated namedRange.
var range = ss.getRangeByName("sampleNamedRange"); // Added
range.setValues(values);
Result:
Note:
The point is to retrieve the range from the updated named range. By this, the updated range can be used. So in above script, the updated value is put to the row 3.
References:
getRangeByName()
Class NamedRange
If I misunderstood your question and this was not the result you want, I apologize
In my Google spreadsheet, in column H I have a list of IP numbers. What I need to a script what will take those numbers and fetch the Country name. I need this to be script since the spreadsheet gets populated from a web-form. What I have is a formula that does that perfectly but I have to copy the formula manually with each form submission. Since I get lots of submissions per day, it's tedious to do this manually. I need the script to run on spreadsheet update. The formula I have is this:
=query( importhtml("http://whatismyipaddress.com/ip/" & H1526, "table", 2), "select Col2 where Col1 = 'Country:' ", 0 )
The last populated row is H1526. I tried the "ARRAYFORMULA" but no array formulas seems to work with my query function. The column where I have the IPs is H and I need the country name in column I.
You can write script on submit of the Google form. Also you need to set a trigger for the same. Hope this helps.
function OnSubmit(e){
var sheetDatabase = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheetDatabase.getRange(sheetDatabase.getLastRow(), 8).setFormula('=query(importhtml("http://whatismyipaddress.com/ip/"&I'+sheetDatabase.getLastRow()+', "table", 2), "select Col2 where Col1 = \'Country:\' ", 0 )');
}
I'm looking for a way to consolidate specific row values from multiple spreadsheets when certain validation are met.
Scenario: I currently have Sheet1, Sheet2, Sheet3
what I want is to consolidate the data from sheet2 and sheet3 into sheet1 when the validation is met.
Sheet1:
Sheet2:
Sheet3:
Based on the example above, I used this function in sheet1 cell A2
=iferror(query(Sheet2!A2:E, "where E = 'New Player' ",0),"")
so what happens is it automatically adds the row values from Sheet2 if Column E "Type" is "New Player" (validation).
What I want is I would also like to add sheet3 row data if its type is "New Player".
maybe there's a way to nest query function? or can this be done using script? I found some scripts that is able to copy rows but without the validation and not from multiple sheets.
Assuming you are querying within the same spreadsheet, something like this should work
=iferror(query({Sheet2!A2:E; Sheet3!A2:E}, "where Col5 = 'New Player' ",0),"")
EDIT: And if you want to pick which columns to return (example A, B, C but not the rest)
=iferror(query({Sheet2!A2:E; Sheet3!A2:E}, "Select Col1, Col2, Col3 where Col5 = 'New Player' ",0),"")