How to display zero in place of error in filter formula in google spreadsheet - google-apps-script

I am new to Google spreadsheet functions and trying to apply a formula in following way:
I have sheet1 & sheet2 in one workbook (name-formula4). I am doing sumproduct of filtered range from sheet1 to sheet2 with the following formula:
=SUMPRODUCT(filter(Sheet1!$A$1:$A$401, (Sheet1!$B$1:$B$401>= E1) * (Sheet1!$B$1:$B$401<= E2)))
Formula is perfectly ok when the range selected
(Sheet1!$B$1:$B$401>= E1) * (Sheet1!$B$1:$B$401<= E2)
has some value & is not empty. However if I leave the cells of said range empty (sheet1!b1:b3) -which are required to meet criteria- then this gives the error No Matches are found in filter evaluation. In a nut shell, I want to display 0 rather than displaying the error or NA.
Note that I cannot fill entire the selected B column since this receives live data from the form and not previous decided which one.
Please look at the following link for details and help in correcting the above formula:
https://docs.google.com/spreadsheets/d/1HTXf4VG2JupiP9UqCLRyAddYjhsom1leQOI9-DwfMaY/edit#gid=0

=IFERROR(SUMPRODUCT(filter(Sheet1!$A$1:$A$401, (Sheet1!$B$1:$B$401>= E1) * (Sheet1!$B$1:$B$401<= E2))),0)

I've had the same issue with my sheets too. I agree with the comments above hiding the error isn't best practice. However hiding the N/A response / customising it is very useful! You need to wrap the whole logic in a NA statement so it starts with it first. Here is how you can do something like this with the formula you are using.
=IFNA(SUMPRODUCT(FILTER((Sheet1!$A$1:$A$401,
(Sheet1!$B$1:$B$401>= E1)*(Sheet1!$B$1:$B$401<= E2))),
"input text or response you want here")
Change "input text or response you want here" to what you want to respond. If you just want it to return a numerical value, like 0, you can remove the quotes at the end and the text response and just put the number. Don't forget the comma (,) after the bracket though!
It should look like this:
=IFNA(SUMPRODUCT(FILTER((Sheet1!$A$1:$A$401,
(Sheet1!$B$1:$B$401>= E1)*(Sheet1!$B$1:$B$401<= E2))),0)

I do NOT recommend IFERROR because it will suppress all type of errors. And that is not a good practice.
What you should be doing is...
IF the filter returns 0 items, THEN just display 0
ELSE, display sum of all the items.
=IF(COUNT(FILTER(_range_,_condition_)) = 0, 0, SUM(FILTER(_range_,_condition_)))

Related

I need to clean up and split words from a mess of data into their own cells in a row. How can I accomplish this?

I am attempting to create documentation from an export of data that gives me a jumbled mess all in one cell that I need to clean up and extract certain bits from.
Here is an example:
[{"label":"Native Invoice","value":"native_invoice","displayOrder":0,"hidden":false,"readOnly":false},{"label":"Data Sync","value":"data_sync","displayOrder":1,"hidden":false,"readOnly":false}]
All of this is in one cell, and I need to have only the following information in their own individual rows:
Native Invoice
Data Sync
This example only has 2 values, but some that I am working on have hundreds, and it is taking far too long to manually copy and paste the values I need into their own cells.
Note: I am working in Google Sheets exclusively.
If I'm understanding you correctly, you want to pull anything after "label": without quotes. If that's the case, and if you are open to a formula instead of a script, supposing that your raw-data block were in A1, place this in B1:
=ArrayFormula(IFERROR(QUERY(FLATTEN(REGEXREPLACE(IF(NOT(REGEXMATCH(SPLIT(REGEXREPLACE(A1,"label.:.([^"&CHAR(34)&"]+)","~|$1~"),"~"),"\|")),,SPLIT(REGEXREPLACE(A1,"label.:.([^"&CHAR(34)&"]+)","~|$1~"),"~")),"\|","")),"WHERE Col1 Is Not Null")))
Here is how a custom function can look like:
function parse(txt) {
var jsn = JSON.parse(txt);
return [jsn[0].label, jsn[1].label];
}
Here is how it works:
You put the data into cell A1, put the formula =parse(A1) into the cell B1, and get the results in cells B1 and B2.
Update
If you want to get labels from all objects of the data, here is another variant of the function:
function get_labels(txt) {
return JSON.parse(txt).map(x => x.label); // get 'label' from all the objects
}
It works about the same way:

Format a Google Sheets cell in numerical formatting 000 via Apps Script

I'm looking to set a column to format 000, which will display the zeros at begenning.
So, if a cell displays "3", I want that the script will set it to display "003".
This column is located in BDD tab, 13th column starting from the second row.
function FormattingGpeTrait() {
const sheet = SpreadsheetApp.getActiveSheet().getSheetByName("BDD").getRange(2,13)
sheet.setNumberFormat('000')
Modification points:
The method of "getSheetByName" is for Class Spreadsheet. In your showing script, you try to use it to Class Sheet. By this, an error occurs. This has already been mentioned in the comment. Ref
From 13th column starting from the second row., I thought that you might have wanted to set the number format of 000 to "M2:M". In your showing script, the number format is set to only a cell "M2".
If you want to set the number format to the cells "M2:M" of the sheet name of "BDD", how about the following modification?
Modified script:
function FormattingGpeTrait() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("BDD");
sheet.getRange("M2:M" + sheet.getLastRow()).setNumberFormat('000');
}
When you run this script, the number format of "000" is set to the cells "M2:M" of "BDD" sheet.
If you want to set the number format to the "M2:M", please modify getRange("M2:M" + sheet.getLastRow()) to getRange("M2:M").
References:
getActiveSpreadsheet()
getSheetByName(name)
The easiest way to get a range on a named sheet is to include the sheet name in the range reference, like this:
function formattingGpeTrait() {
SpreadsheetApp.getActive().getRange('BDD!M3:M').setNumberFormat('000');
}
I think that you can't use the standard number formats as they all will only evaluate your value to a real number value where '003' in reality is equal to '3' from a numeric sense.
You have two real options which is to either store the value in a Text column as "003" or prepend the value with an apostrophe "'003" which is basically the same as storing it as Text but the column can remain numeric.
You can create a custom number format for a cell/column to also do this but I am not certain how to accomplish this programatically. Basically, this is still going to end up like the Text variations I mention above, only you have a named format you can call. The data will still be stored as Text.

Google Sheets Join information from two pages with query & vlookup

I know this has been asked several times, but I just can't seem to understand how to write the formula and I'm hoping to get some help.
Consider the following (example data) sheet:
https://docs.google.com/spreadsheets/d/1t_I_stZmZea4sfGPsCu6GtBhGJJT16CZ-sEu7JubFKc/edit?usp=sharing
First, note that I am importing data on "API Data" utilizing importJSON().
My goal is to combine (join) data from two sheets. I need "dataseries cloudcover" from 'API data' and "Dataseries example,Dataseries example 1,Dataseries example 2" from 'join'.
I gave it a shot here:
=query('API data'!A:L,"Select " & vlookup(B:B,'API data'!B:L,3,FALSE) & ",B,C,D,E,F,G,H,I,J,K,L")
Here is a SS of what I would like to see
This formula can help you to get that data:
Note: Just add the formula in A2
={ARRAYFORMULA(IF(ISBLANK('API data'!C2:C),"",ARRAYFORMULA(VLOOKUP('API data'!C2:C,'API data'!C2:D25,2)))),ARRAYFORMULA(IF(ISBLANK(Join!A2:A),"",ARRAYFORMULA(VLOOKUP(Join!A2:A,Join!A2:D25,{2,3,4},FALSE))))}
And it will look like this:
Edit:
Editing and adding more information about the use of this formula.
The formula is constructed with 2 different VLookUps, 1 for each tab, and they are merged using:
={First Array, Second Array}
The first Array is:
ARRAYFORMULA(IF(ISBLANK('API data'!K2:K),"",ARRAYFORMULA(VLOOKUP('API data'!K2:K,'API data'!K2:L25,2))))
The second Array is:
ARRAYFORMULA(IF(ISBLANK(Join!I2:I),"",ARRAYFORMULA(VLOOKUP(Join!I2:I,Join!I2:L25,{2,3,4},FALSE))))
The core part of the first array for this formula is:
ARRAYFORMULA(VLOOKUP('API data'!K2:K,'API data'!K2:L25,2))
The IF(IsBlank(column,"",Vlookup) will remove any empty value of the Array.
The same thing with the second Array, with the difference that I use an Array {2,3,4} to call all the columns in the second sheet.
Reference:
VLOOKUP function.
ARRAYFORMULA function.
IF function.
ISBLANK function.

Replace INDIRECT() in data validation rule

I had a working Excel spreadsheet that used indirect() in the data validation and it worked fine. I uploaded it to sheets and converted it, now the indirect does not work.
I have found a link on the support forum that explains it does not work in Chrome but appears to work in Firefox, and the answers and workarounds seem to be for generating a secondary list... which is what I want, but in a data validation across a row.
I have knocked up a simple test sheet, hopefully public and the script editor is visible:
https://docs.google.com/spreadsheets/d/1KUgrdXKIKlk1DWvDOX9cY3B2VnRH_5h_vKuZJlqUlN8/edit?usp=sharing
Hopefully you can see what I'm after. I want the validation in C8 to be the list of items in the category based in B8; C9 based on B9 etc.
EDIT and Update
The question is about a replacement to indirect() in a data validation rule. While I did find a way round this by using indirect(), I preferred the version mentioned by Desire (to whom I have attributed the answer), but I thought I'd document my solution in case the sheet above becomes unavailable, or you cannot access it, or you just wanted a bit more detail.
So, for My Demo I have this:
In A1:C5 are my lists of data with the titles.
In the range B8:B12 I applied a data validation rule of value in range of A1:C1 - this gives the first dropdown.
In Cell E8 I put the formula =transpose(filter($A$2:$C$5, $A$1:$C$1 = B8)) and then copied this down to E12
Finally I put the following in a function and ran it in the script editor.
function runMeOnce() {
var dst = SpreadsheetApp.getActive().getSheetByName('Sheet1').getRange('C8:C12');
var rules = [];
for (var i = 8; i < 13; i++) {
var src = SpreadsheetApp.getActive().getSheetByName('Sheet1').getRange("E" + i + ":H" + i);
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(src).build();
rules.push(rule);
}
dst.setDataValidations(rules);
}
That's all there is, no more onEdit() triggering.
NOTE There is one downside I bumped into with this method though. I have this in place for 6000+ rows in my actual spreadsheet, and across multiple sheets, with some dropdowns having 50-100 items in. This solution seriously eats into the (current) 2 million cell limit.
Hope this helps someone.
Data Validation rule of the type "List of items" takes only a comma-separated list of values as its parameter, and does not evaluate any formulas you try to put there. It does not matter what the function returns, because it will not be called. If you put, say "=sqrt(A10)" in the field "List of items", that only means that the validation rule will require the string "=sqrt(A10)" to be entered in the cell.
Similarly with "List from a Range". Either what you enter parses as range notation, or it does not. The string "=getValidationRange(B8)" does not parse as range notation, hence the error. The function is never called.
The only type of validation that calls a function is "Custom formula". If you use it, then the validation can be performed as intended: for example,
=match(C8, filter(A2:C5, A1:C1 = B8), 0)
requires the content of C8 to be in the column of the table A2:C5 under the heading that matches the category in B8. However, with a custom formula you do not get a dropdown in a cell.
To get a dynamic dropdown, one can either
Use an auxiliary range
For example, enter filter(A2:C5, A1:C1 = B8) in cell F1, so that the F column is for the categories currently selected. The data validation would be "List from a Range", F1:F. This is a fine workaround for one validation rule, but takes more work when you have multiple ones.
Use a triggered script
Use a script that is triggered on edit and sets data validation rules accordingly; this is discussed in How do you do dynamic / dependent drop downs in Google Sheets? among other places.
Based on the sacrificing a goat issue, I did find a simple(ish) way around the problem that still uses indirect().
Set up the named ranges as previously using the titles in CamelCase. In my example I have CatA, CatB, and CatC - i.e. the white space needs removing.
At the end of a row (or in another sheet) transpose the chosen named range (in cell E8: =transpose(indirect(substitute(B8, " ", ""))) copy this down as far as you need.
At this point it's good to note that because we are unsing builtin functions, the speed is so much better, as can be seen by my example.
Now the painful bit. For each subcategory cell (C8, C9 etc in my example), you need to add the validation independently as a range of E8:ZZ8 (obviously ZZ8 needs reigning in a bit) and E9:ZZ9 etc. It doesn't seem to do referential so if you select all the cell in the column, they all only look at the data you specifically type in the box... I might just not have worked out R1C1 notation here, however. I tried.
This can be scripted on GAS to create the R1C1 validation function and then apply it to the range.

Insert formula in a Google Sheets programmatically

I need to put a formula into a cell in each new row added to a Google Sheets. I have this working in VBA but not been able to build it correctly in Script.
I loop through i rows until lastrow. In cell J, I want this formula inserted:
var Discount = '=IF(ISBLANK("F"+i,,IF(ISNUMBER(FIND("CM","B"+i)),IF("C"+i>"F"+i,150,0),0))';
I use this method to add the row:
var dtaCollect = ["","",StartDate,CustomerName,Monthly,"",Discount,LateFee,TotalPaid,Commission,Note,Referral];
target_sheet.appendRow(dtaCollect);
i++;
} else {
i++;
}
}
However, the formula is written exactly as above, without i substituted with the iteration value. As a result I get #ERROR! in the cell. I've tried INDIRECT and concat.
How can I fix this?
The value i isn't being substituted in your string because it's just text. You need to break it out of the string, and be more careful with your use of quotes to ensure you end up with a viable formula. This would work:
var Discount = '=IF(ISBLANK(F'+i+',,IF(ISNUMBER(FIND("CM",B'+i+')),IF(C'+i+'>F'+i+',150,0),0))';
Since you're using A1Notation, a simple JavaScript String.replace() should be all you need to provide a more readable solution:
var Discount = '=IF(ISBLANK(F%row%,,IF(ISNUMBER(FIND("CM",B%row%)),IF(C%row%>F%row%,150,0),0))'
.replace(/%row%/g, i.toString());
Explanation:
replace() will find regexp or substring matches, and replace them with a new substring.
in this case, we're looking for a regexp; the g flag means we'll look for all occurrences of "%row%" and replace them with the value of i.
We've used the % as bookends, to make the replaceable text stand out clearly - just a convention, not a requirement.
Note: You didn't show how you used INDIRECT, only mentioned that you tried it. It is an alternative here, and might be preferred as you could simply copy a formula from an existing cell without worrying about adjusting the references.