COUNTA not working as I expect in new Google Sheets - google-sheets

I'm using the command =COUNTA(I31:I) to count all non-empty cells in the column. The cells in the column contain this line of code:
=IF(ISNUMBER(D31), "X", "")
The result is that the cells are either an empty string (the line with add nothing "") or contain an X.
The problem now is that COUNTA counts all the cells after this procedure, empty or not. If I delete the long command line the cell is not counted and it works fine. So this long command line is adding something to the cell, so it appears not-empty even though it looks empty.
Any clues to what that is? My guess is that I should use some kind of Null operator instead of the "" to add nothing to the cell.
Note: For some reason, this seems to work in a previous version of Google Spreadsheet I had.
As a potential workaround, I could replace =COUNTA(I31:I) by checking specifically for x with =COUNTIF(I31:I;"X"). But I'm still curious as to the problem with COUNTA.
If it turns out that my cells are not empty 'enough' for this command, how can I then make them completely empty?

See Also: Count rows with not empty value
Unfortunately, this is functions as designed from Google.
Although I'm not sure why it's divergent from the way that Excel calculates COUNTA.
According to the Google Spreadsheet Documentation on COUNTA:
COUNTA counts all values in a dataset, including those which appear more than once and text values (including zero-length strings and whitespace).
Meaning that the only way to "make [the cells] completely empty" is to delete the entire contents, formula and all. But fear not...
Some workarounds:
Hypothetically, you should be able to do this with =COUNTIF(A3:A8,"<>"&""), but Google spreadsheets doesn't support the not equal to operator in the COUNTIF function according to these forums: 1, 2, 3
A workaround is to create a truthy or falsy array based on the condition and use SUMPRODUCT to count the truthy values like this:
=SUMPRODUCT((A3:A8<>"")*1)
Another option you could pursue would be to write a custom function and add it to Drive.
It's actually pretty easy to do so. Go to Tools > Script Editor and add the following script:
/**
* Works like COUNTA except does not include null strings
*
* #param {cellRange} myArray The value or range to consider when counting.
* #return Returns the a count of the number of values in a dataset.
* #customfunction
*/
function CountNotBlank(myArray) {
var output = 0;
for (i = 0; i < myArray.length; i++) {
if (myArray[i] != "") {
output += 1
}
}
return output;
}
Then use like this:
=CountNotBlank(I31:I)

I would try: =IF(ISNUMBER(D31), "X", iferror(1/0))
I am told that the iferror(1/0) returns nothing at all.

Related

How to give a cell containing with 'ISREF' a default value in Google Sheets

I am trying to use a formula that uses INDEX function. In order to generalize for all the lines that sometimes may contain information from another sheet and sometimes they may not.
That's why I get an ISREF error within the cells because the corresponding cells in the other sheet can't be referenced.
I want to display a default value instead of the ISREF error message. I tried using ISREF function itself within an IF condition but it doesn't work on the same cell. It only references another cell because it is a cell checking function and it doesn't check the output of a formula.
I tried also ISERROR function but it didn't work also. Here's a snippet of the formula that I am putting within my cell:'
INDEX(Plagesuivi; $Q203; 9)
Plagesuivi is a named range
$Q203 contains the row number (that I fetch dynamically and correctly)
9 is the column number
P.S. The indexing is working fine with cells that do appear is the named range: Plagesuivi
I would go with iferror() like this:
=iferror(INDEX(Plagesuivi;$Q203;9);0)
Or replace the 0 with ""
After trial and error the best way to avoid all sorts of errors is:
= IF($Q203=""; 0; IFERROR(INDEX(Plagesuivi; $Q203; 9); 0))
IFERROR checks for all sorts of unpredictable errrors it is a safe-guard against unpredicted cases where it takes 0 by default.
IF in the second case checks whether the referencing content of Q203 is empty or not, in case it is empty the cell takes 0 by default else it gets the output of the false case formula.

Google spreadsheet countif non empty cells except certain values

So basically i want to check if certain range of field is not blank or has a certain value
so
=ArrayFormula(COUNTIF(NOT(ISBLANK(H24:I28)), true))
will count if they are blank
How can i edit it to also search for a certain value so far i've tried :
=ArrayFormula(COUNTIF(NOT(ISBLANK(H24:I28)) OR(IFERROR(SEARCH("someValue",H24:I28,1)>0,FALSE)), true))
Here is a link to a sheet as an example:
https://docs.google.com/spreadsheets/d/1sc8xmLf8_EYFoQb3kNQRdxdd-9PemrJ4lDhTfIqCJNg/edit?usp=sharing
Update following OP's details:
Well i want to count every value except anything that starts with the word Empty basically
=COUNTIF(H24:I29,"<>") -
COUNTIF(ArrayFormula(REGEXMATCH(H24:I29,"Empty")),TRUE)
Functions used:
REGEXMATCH
ArrayFormula
COUNTIF
Initial answer
To find whether theValue exists or not, you can use the following formula:
=IF(COUNTIF(H24:I28,"<>")>0,IF(COUNTIF(H24:I28,"someValue")>0,"someValue","no value"),"empty")
To count how many times theValue exists, please use:
=IF(COUNTIF(H24:I28,"<>")>0,IF(COUNTIF(H24:I28,"someValue")>0,COUNTIF(H24:I28,"someValue"),"no value"),"empty")
(Of course you can adjust the "messages" to your liking)
Functions used:
COUNTIF
IF
You could use the OR() operator:
Example:
=ArrayFormula(OR(COUNTIF(NOT(ISBLANK(H24:I28)),true),IFERROR(SEARCH("someValue",H24:I28,1)>0,FALSE)))
References:
- OR() function

How to compare if the last trail of two URLs match?

I have tried to create a spreadsheet which lets me know if the last trailing step of two URLs are matching. (In case you are wondering: It is for redirect mapping because when I have old URLs and match them to a new structure the last trail stays the same more often than not.)
The said spreadsheet is working more or less and can be found here: https://docs.google.com/spreadsheets/d/1m3E5NQYSUGe4Kxn4BrpcgWj4WhotKG88JCx93ER3U1c/edit?usp=sharing
I did the following:
Pull old and new URLs in two separate sheets (oldcrumb/newcrumb)
Split the URLs in these sheets into separate cells splitting at every "/"
Compare the last filled Cell in a Row between the two sheets
Unfortunately my solution is somewhat clunky. For about 600 initial rows I need another 1200 rows because I use 2 different sheets and also use split formula to get hands on the last trail.
Also I have just implemented comparison for a certain range (Rows I to H in the split row sheets) of trailing position with several IF-conditions. If URLs are supershort or superlong, nothing will be displayed in the "Last trail matching?" row.
Also at some point I get a warning because I would reach the maximum amount of usable cells in Google Sheets (around 200000 or so?).
So is there any more elaborate/elegant way to do what I did in a super awkward and heavy loaded approach?
The following formula does it:
=regexreplace(A2, ".*/", "")=regexreplace(C2, ".*/", "")
Here, regexreplace removes everything up to (and including) the last slash, because .* means any number of any characters. So the equality is tested between the tails after the last slash.
Also works as an array formula:
=arrayformula(regexreplace(A2:A11, ".*/", "")=regexreplace(C2:C11, ".*/", ""))
or an array formula that allows for blank row in the input range:
=arrayformula(if(isblank(A2:A), "", regexreplace(A2:A, ".*/", "")=regexreplace(C2:C, ".*/", "")))
I would go with a compare method using lastIndexOf() and slice()
Something like:
/**
* Added for use as a custom function
* #customfunction
*/
function compareurl (oldurl, newurl) {
var a = oldurl.slice(oldurl.lastIndexOf("/") + 1);
var b = newurl.slice(newurl.lastIndexOf("/") + 1);
return (a == b) ? true : false;
}
It finds the last / and creates a new string from the last token in the url to compare
edit Just noticed you may not be using apps scripts. Would still go with a Custom Function and the above method. Then use =compareurl()

Indirect Addresses in Array Formula

I have the following formula
=average(arrayformula(indirect(split(A1,","))))
Where A1 contains a list of cell addresses, such as E4,E6,E12. I expect this to be equivalent to =AVERAGE(E4,E6,E12), but this does not behave as expected, yielding 4 no matter what the data in the cells are. Preliminary research indicates that the INDIRECT() function doesn't pass through ARRAYFORMULA() correctly. Attempting SUM() on the outside yields precisely the same results.
Any ideas on how to average the values of cells obtained indirectly by a list of cell addresses?
I do have a list of columns and the row doesn't ever change for this average calculation, so I'm wondering if I could do some kind of subset instead, such as
=AVERAGE(RANGE){LIST_TO_SUBSET_BY}
I'm not sure about a built-in formula to do this so I've written a custom function to do it for you.
Go to Tools -> Script editor and replace the existing function with the code below and then save the project.
Now in your spreadsheet in any cell =CUSTOMFUNCTION(A1) where A1 contains a list of comma-separated cell references.
NOTE:
Updating values in the referenced cells won't force a recalculation of this formula, only updating cell A1 will.
I suggest you also go to File -> Spreadsheet settings -> Calculation and change 'Recalculation' to 'On change and every minute' that will force a recalculation of this function every minute.
/**
* Returns the average value of a dataset.
* #param {"A1"} cell The cell containing the list of cell references.
* #return The input repeated a specified nunmber of times.
* #customfunction
*/
function CUSTOMAVERAGE(cell){
var ss = SpreadsheetApp.getActiveSheet();
var array = [];
var cellRefs = cell.split(",");
for(var i in cellRefs){
array.push(ss.getRange(cellRefs[i]).getValue());
}
var sum = 0;
for(var i in array){
sum += array[i]
}
var avg = sum/array.length;
return avg;
}
Though this is a very specific application in response to this question, for the sake of the knowledge base, I'd like to show how this can be done without a script.
To give this context, imagine the LIST_CELL is a list of question numbers
(which are entered in as a header row, call the range QUESTIONS) on a test that correspond to certain standards, and the goal is to average only the questions that correspond to the standard next to which the list is written, and for each student. Using
=iferror(join(",",ArrayFormula(match(split(LIST_CELL,","),QUESTIONS,FALSE))),"")
The split function splits the a hand-entered list of questions on commas, the match function returns the column number of that particular question in QUESTIONS, and the join function joins the data back together. ArrayFormula allows the match to be performed on an array instead of just the first value.
Another single row heading lists the standards to which each question has been matched (possibly to more than one standard) by the comma separated list in LIST_CELL. For a column list of students in A:A, each standard needs to average the scores of every question that is listed next to the standard. This is accomplished by the nifty (if clunky):
average(ArrayFormula(hlookup(split(vlookup(LOOKUP_VAL,SEARCH_RANGE,COL_W_LIST),","),DATA_SOURCE,row(CURRENT_CELL))))
Breakdown from center outward:
LOOKUP_VAL is the value being looked up (the one that has multiple matches); in the example context, it's the standard.
SEARCH_RANGE is a range of cells containing both the list of lookup value (the standards in context) and the comma separated lists of column numbers generated by the first function. COL_W_LIST is the column number in the array SEARCH_RANGE that contains the list of row numbers matched from LIST_CELL.
Split takes the elements apart and placed them in a temporary array so that hlookup can be performed on each element. Via ArrayFormula the hlookup grabs each value on the same row in the appropriate QUESTIONS column - in context, it grabs the point scores for each question matched to the standard.
Finally, average is self-explanatory, and does take an array as input apparently.
These two functions in combination allow of use of indirect cell references in an array formula, and solves the much asked, "how do I include multiple matches in a calculation" question. At least in this specific context.
EDIT
There is an example "template" with this implemented here. You'll need to make your own copy to edit it.

How to highlight cell if value duplicate in same column for google spreadsheet?

I am looking for formula for google spreadsheet
highlight cell if value duplicate in same column
can anyone please assist me for this query?
Try this:
Select the whole column
Click Format
Click Conditional formatting
Click Add another rule (or edit the existing/default one)
Set Format cells if to: Custom formula is
Set value to: =countif(A:A,A1)>1 (or change A to your chosen column)
Set the formatting style.
Ensure the range applies to your column (e.g., A1:A100).
Click Done
Anything written in the A1:A100 cells will be checked, and if there is a duplicate (occurs more than once) then it'll be coloured.
For locales using comma (,) as a decimal separator, the argument separator is most likely a semi-colon (;). That is, try: =countif(A:A;A1)>1, instead.
For multiple columns, use countifs.
While zolley's answer is perfectly right for the question, here's a more general solution for any range, plus explanation:
=COUNTIF($A$1:$C$50, INDIRECT(ADDRESS(ROW(), COLUMN(), 4))) > 1
Please note that in this example I will be using the range A1:C50.
The first parameter ($A$1:$C$50) should be replaced with the range on which you would like to highlight duplicates!
to highlight duplicates:
Select the whole range on which the duplicate marking is wanted.
On the menu: Format > Conditional formatting...
Under Apply to range, select the range to which the rule should be applied.
In Format cells if, select Custom formula is on the dropdown.
In the textbox insert the given formula, adjusting the range to match step (3).
Why does it work?
COUNTIF(range, criterion), will compare every cell in range to the criterion, which is processed similarly to formulas. If no special operators are provided, it will compare every cell in the range with the given cell, and return the number of cells found to be matching the rule (in this case, the comparison). We are using a fixed range (with $ signs) so that we always view the full range.
The second block, INDIRECT(ADDRESS(ROW(), COLUMN(), 4)), will return current cell's content. If this was placed inside the cell, docs will have cried about circular dependency, but in this case, the formula is evaluated as if it was in the cell, without changing it.
ROW() and COLUMN() will return the row number and column number of the given cell respectively. If no parameter is provided, the current cell will be returned (this is 1-based, for example, B3 will return 3 for ROW(), and 2 for COLUMN()).
Then we use: ADDRESS(row, column, [absolute_relative_mode]) to translate the numeric row and column to a cell reference (like B3. Remember, while we are inside the cell's context, we don't know it's address OR content, and we need the content in order to compare with). The third parameter takes care for the formatting, and 4 returns the formatting INDIRECT() likes.
INDIRECT(), will take a cell reference and return its content. In this case, the current cell's content. Then back to the start, COUNTIF() will test every cell in the range against ours, and return the count.
The last step is making our formula return a boolean, by making it a logical expression: COUNTIF(...) > 1. The > 1 is used because we know there's at least one cell identical to ours. That's our cell, which is in the range, and thus will be compared to itself. So to indicate a duplicate, we need to find 2 or more cells matching ours.
Sources:
Docs Editors Help: COUNTIF()
Docs Editors Help: INDIRECT()
Docs Editors Help: ADDRESS()
Docs Editors Help: ROW()
Docs Editors Help: COLUMN()
Answer of #zolley is right. Just adding a Gif and steps for the reference.
Goto menu Format > Conditional formatting..
Find Format cells if..
Add =countif(A:A,A1)>1 in field Custom formula is
Note: Change the letter A with your own column.
From the "Text Contains" dropdown menu select "Custom formula is:", and write: "=countif(A:A, A1) > 1" (without the quotes)
I did exactly as zolley proposed, but there should be done small correction: use "Custom formula is" instead of "Text Contains".
And then conditional rendering will work.
Highlight duplicates (in column C):
=COUNTIF(C:C, C1) > 1
Explanation: The C1 here doesn't refer to the first row in C. Because this formula is evaluated by a conditional format rule, instead, when the formula is checked to see if it applies, the C1 effectively refers to whichever row is currently being evaluated to see if the highlight should be applied. (So it's more like INDIRECT(C &ROW()), if that means anything to you!). Essentially, when evaluating a conditional format formula, anything which refers to row 1 is evaluated against the row that the formula is being run against. (And yes, if you use C2 then you asking the rule to check the status of the row immediately below the one currently being evaluated.)
So this says, count up occurences of whatever is in C1 (the current cell being evaluated) that are in the whole of column C and if there is more than 1 of them (i.e. the value has duplicates) then: apply the highlight (because the formula, overall, evaluates to TRUE).
Highlight the first duplicate only:
=AND(COUNTIF(C:C, C1) > 1, COUNTIF(C$1:C1, C1) = 1)
Explanation: This only highlights if both of the COUNTIFs are TRUE (they appear inside an AND()).
The first term to be evaluated (the COUNTIF(C:C, C1) > 1) is the exact same as in the first example; it's TRUE only if whatever is in C1 has a duplicate. (Remember that C1 effectively refers to the current row being checked to see if it should be highlighted).
The second term (COUNTIF(C$1:C1, C1) = 1) looks similar but it has three crucial differences:
It doesn't search the whole of column C (like the first one does: C:C) but instead it starts the search from the first row: C$1
(the $ forces it to look literally at row 1, not at whichever row is being evaluated).
And then it stops the search at the current row being evaluated C1.
Finally it says = 1.
So, it will only be TRUE if there are no duplicates above the row currently being evaluated (meaning it must be the first of the duplicates).
Combined with that first term (which will only be TRUE if this row has duplicates) this means only the first occurrence will be highlighted.
Highlight the second and onwards duplicates:
=AND(COUNTIF(C:C, C1) > 1, NOT(COUNTIF(C$1:C1, C1) = 1), COUNTIF(C1:C, C1) >= 1)
Explanation: The first expression is the same as always (TRUE if the currently evaluated row is a duplicate at all).
The second term is exactly the same as the last one except it's negated: It has a NOT() around it. So it ignores the first occurence.
Finally the third term picks up duplicates 2, 3 etc. COUNTIF(C1:C, C1) >= 1 starts the search range at the currently evaluated row (the C1 in the C1:C). Then it only evaluates to TRUE (apply highlight) if there is one or more duplicates below this one (and including this one): >= 1 (it must be >= not just > otherwise the last duplicate is ignored).
I tried all the options and none worked.
Only google app scripts helped me.
source : https://ctrlq.org/code/19649-find-duplicate-rows-in-google-sheets
At the top of your document
1.- go to tools > script editor
2.- set the name of your script
3.- paste this code :
function findDuplicates() {
// List the columns you want to check by number (A = 1)
var CHECK_COLUMNS = [1];
// Get the active sheet and info about it
var sourceSheet = SpreadsheetApp.getActiveSheet();
var numRows = sourceSheet.getLastRow();
var numCols = sourceSheet.getLastColumn();
// Create the temporary working sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var newSheet = ss.insertSheet("FindDupes");
// Copy the desired rows to the FindDupes sheet
for (var i = 0; i < CHECK_COLUMNS.length; i++) {
var sourceRange = sourceSheet.getRange(1,CHECK_COLUMNS[i],numRows);
var nextCol = newSheet.getLastColumn() + 1;
sourceRange.copyTo(newSheet.getRange(1,nextCol,numRows));
}
// Find duplicates in the FindDupes sheet and color them in the main sheet
var dupes = false;
var data = newSheet.getDataRange().getValues();
for (i = 1; i < data.length - 1; i++) {
for (j = i+1; j < data.length; j++) {
if (data[i].join() == data[j].join()) {
dupes = true;
sourceSheet.getRange(i+1,1,1,numCols).setBackground("red");
sourceSheet.getRange(j+1,1,1,numCols).setBackground("red");
}
}
}
// Remove the FindDupes temporary sheet
ss.deleteSheet(newSheet);
// Alert the user with the results
if (dupes) {
Browser.msgBox("Possible duplicate(s) found and colored red.");
} else {
Browser.msgBox("No duplicates found.");
}
};
4.- save and run
In less than 3 seconds, my duplicate row was colored. Just copy-past the script.
If you don't know about google apps scripts , this links could be help you:
https://zapier.com/learn/google-sheets/google-apps-script-tutorial/
https://developers.google.com/apps-script/overview
I hope this helps.

Resources