Related
Editable Test Sheet:
https://docs.google.com/spreadsheets/d/1zKtE09TB-mAEQFRswity3R1ZGdYe7jq6ZYh_l4P398E/edit?usp=sharing
Although I believe what I'm trying to do is fairly simple, It is difficult for me to even find the correct words to describe what I'm trying to do. I suspect that is why I've been researching all day for an answer and cannot find it.
I need an ARRAYFORMULA that can check for the existence of a given "PARENT ID" in another table, but only if another cell in the same row is not true.
A given PARENT ID from TABLE 2 could appear in TABLE 1 multiple times, sometimes paired with "TRUE" and sometimes not. I only need to know if the PARENT ID from TABLE 1 appears in TABLE 2 along with the value TRUE in the "Done" column next to it. If it appears even once, I want to denote it in TABLE 2.
I am able to do this in various forms, but none of them work with ARRAYFORMULA.
See the shared example sheet above. I would greatly appreciate any help.
See my two newly added sheets ("Erik Help" and "Erik Help 2"). Below are the formulas I used:
In "Erik Help":
=ArrayFormula({"In Table 1 and Not Done?";IF(E3:E="",,IF(ISERROR(VLOOKUP(E3:E,TRIM(B3:B&C3:C),1,FALSE)),,TRUE))})
This delivers the results you want as asked (though your manually entered results did not list F3 as TRUE when I believe it should be).
The trick here is looking for the Parent ID within a virtual column that concatenates Col B and Col C and then TRIMs out extra characters (such as null). If there is an exact match, we know that there was no value in Col B. And since the only valid Col-B value is Boolean TRUE, we are assured that any match is in effect false (or not Done).
In "Erik Help 2":
=ArrayFormula({"In Table 1 and Not Done?";IF(E3:E="",,IF(ISERROR(VLOOKUP(E3:E,TRIM(B3:B&C3:C),1,FALSE)),,IFERROR("Item ID(s): "&VLOOKUP(E3:E,REGEXREPLACE(TRIM(SPLIT(FLATTEN(QUERY(QUERY({FILTER(C3:C,A3:A<>"",B3:B<>TRUE)&"~",FILTER(A3:A&" (R"&ROW(A3:A)&")",A3:A<>"",B3:B<>TRUE)&","}, "Select MAX(Col2) GROUP BY Col2 PIVOT Col1"),, 9^9)),"~")),"[,\s]+$",""),2,FALSE),TRUE)))})
Instead of simply TRUE, this version returns the Item ID(s) that are not done for that Parent ID along with the row number where each incomplete Item ID is found.
Since you didn't expressly ask for this, I'm considering it bonus material and will not take time to explain it.
Another answer I received on another forum:
=ArrayFormula(IF(COUNTIF(C3:C&B3:B,E3:E),true,))
Credit to Prashanth KV
I am trying to count the unique values of a column, based on their status in another column, example:
Customers
License Active
Adam
Yes
Barry
No
Adam
No
Claire
No
In this situation, I want to know how many customers have at least 1 active license, and how many customers do not have at least one active license.
The formula I have tried is:
=COUNTUNIQUEIFS(A2:A,B2:B,"Yes")
This returns 1 in this situation which is correct, as there is 1 customer who has a Yes on column B.
My issue is when I try to do the reverse, count the "No" using this formula:
=COUNTUNIQUEIFS(A2:A,B2:B,"No") it returns 3 which is not the desired result as it is counting the second Adam as a unique value too because they have a "No" in column B.
The result I want here is 2, because Adam has a yes somewhere in column B so I don't want him counted again the next time his field is counted.
It seems to me that the easiest way to get the "No" count is like this:
=COUNTUNIQUE(A2:A)-COUNTUNIQUEIFS(A2:A,B2:B,"Yes")
It's even easier if you've already pulled the "Yes" count to a cell (say, C2), in which case the "No" count could be gained quite simply with this:
=COUNTUNIQUE(A2:A)-C2
I don't think you can do it in a single step - try filtering out those with at least one "Yes" like this:
=countunique(filter(A2:A,countifs(B2:B,"Yes",A2:A,A2:A)=0))
Explanation
When a countifs has a range instead of a single value in its criteria part countifs(B2:B,"Yes",A2:A,A2:A) , the countifs gets re-evaluated for each cell in the range. So you get an array with the results of
countifs(B2:B,"Yes",A2:A,A2)
countifs(B2:B,"Yes",A2:A,A3)
countifs(B2:B,"Yes",A2:A,A4)
countifs(B2:B,"Yes",A2:A,A5)
and so on all the way down the columns.
The first countifs above checks right through a2:a and b2:b to see if there are any cases where the name is Adam and the license condition is true and gets a count of 1 so that row is filtered out. The same thing happens in the next row containing Adam (row 4) - the countifs checks right through both columns excluding the headers and the count is still 1 so that row is filtered out as well leaving just Barry and Claire.
If you wanted to exclude all records containing "Test" in the Customer column, You could add a condition to the filter using the multiplication operator to 'AND' it with the existing condition:
=countunique(filter(A2:A,(countifs(B2:B,"Yes",A2:A,A2:A)=0)*(A2:A<>"Test")))
If you had several names to exclude, you would probably want to make a list of them and use a lookup to stop the formula getting too long and unwieldy, but it would be the same idea.
I am trying to set up a Google Sheet arrayformula to find any duplicates without identifying the first instance of that value. I have a table with the values below:
A
1
2
3
4
5
Since number 2 is a duplicate value, I would like to identify this with a formula but I would not like to indentify the first value in this column. This formula gets me the results I am looking for: =IF(COUNTIF($A$2:$A2,A2)=1, "Unique", "Duplicate")
A
B
1
Unique
2
Unique
3
Unique
2
Duplicate
4
Unique
2 Duplicate
But when I try to convert this to an arrayformula so I don't have to manually drag the formula down when new rows are added I get a different result. This is the arrayformula I used: =ARRAYFORMULA(IF($A$2:$A="", "", IF(COUNTIF($A$2:$A,A2:A)=1, "Unique", "Duplicate")))
A B
1 Unique
2 Duplicate
3 Unique
2 Duplicate
4 Unique
2 Duplicate
The problem is that the first value is also identified as duplicate. What would be the best way to convert =IF(COUNTIF($A$2:$A2,A2)=1, "Unique", "Duplicate") into an arrayformula?
You want this:
=ArrayFormula(IF(A2:A="",,IF(COUNTIFS(A2:A,A2:A,ROW(A2:A),"<="&ROW(A2:A))=1,"Unique","Duplicate")))
The problem with your COUNTIF is that you essentially asked "Is this number unique against every other number in this column? Or is it duplicated anywhere else in this column?" That is why 2 says "Duplicate" in all instances: because each of them is duplicated "somewhere else" in the column.
What you really want to be asking is "Up to this row, has this number been duplicated yet so far?" And that requires COUNTIFS with a second condition that only checks considering ROW() numbers "up to" (i.e., "<=") the current row.
This is a very nice article to find duplicate entry. But I would like to add one missing point. That is, if you incorporate a TRIM function with the above functions, this will become more accurate and perfect. Because if we add spaces at the beginning or end in the duplicate cells, then the above functions won’t consider it as duplicate.
I did this to monitor my employees work sheets as well. i.e
ARRAYFORMULA(if(len(TRIM(C9:C)),(if((countif(TRIM(C9:C),TRIM(C9:C)))>1,”duplicate”,)),))
I've got the following Google spreadsheet:
item have ready need1 need2 need3
A 1 2 1
B 1 2 1 1
C 2 2
etc
I want to fill ready column as follows:
find the first column in need1, ..., needN range which has a non-empty value
if the value found is less or equals the value in have column, set ready column to something cheerful (e.g. yes)
if the value found is larger than the value in have column, don't do anything
So above input, when processed should look like this:
item have ready need1 need2 need3
A 1 2 1
B 1 2 1 1
C 2 yes 2
For the first step I found a suggested solution, which did not work for me:
=INDEX( SORT( FILTER( D10:H10 , LEN( D10:H10 ) ) ,
FILTER( COLUMN( D10:H10 ) , LEN( D10:H10 ) ) , 0 ) , 1 )
(it returns #REF!) Not sure what's wrong with it or how to proceed to the next step.
Thanks in advance!
If you know how many need columns you have, or even just how many columns are on the sheet, this is quite straightforward. If not and you need to look at the entire row, you might have to redesign a bit to avoid a circular reference from the cell with the formula being part of that row.
Your second two steps are fairly simple either way - you want one of two results based on a condition, so you're going to want to use =IF. Your condition is that the 'need' number is less than or equal to the 'have' number, and you want it to say 'yes' if that's true, and nothing if it isn't. So, that gives us:
=IF(need<=have, "Yes", "")
The examples below assume your table above starts from cell A1 in the top left, and that the last column in your sheet is Z
Next we need to find 'need' and 'have'. Finding 'have' is pretty easy - it's just the number in column B.
Finding 'need' is slightly more complicated. You've got the right idea using INDEX and FILTER, but your formula seems a little overcomplicated. Basically we can use FILTER to filter out the blank values, and INDEX to find the first one that is left. First, FILTER:
The range you want to filter from is everything in the same row from column D to column Z (or whatever the final column is), and the condition you want to filter for is that those same cells are not blank. For the formula you're typing into cell C2, that gives us:
=FILTER(D2:Z2, D2:Z2<>"")
Next, INDEX: If you give INDEX an array, a row number, and a column number, it will tell you what is at that the cell where that row and column meet. As we've filtered out the blanks, we just want whatever is left in the first column of our filtered array, which gives us:
=INDEX(FILTER(D2:Z2, D2:Z2<>""), 1, 1)
Or, as we only have one row in our array, and INDEX is pretty smart, simply:
=INDEX(FILTER(D2:Z2, D2:Z2<>""), 1)
So to bring it all together, our final formula for cell C2 is:
=IF(INDEX(FILTER(D2:Z2, D2:Z2<>""), 1)<=B2, "Yes", "")
Then just drag the formula down for as many rows as you need. If your sheet is or becomes wider, just change Z to whatever your last column is.
When you don't know the size of a range, use functions row, column, rows, columns.
Simple formula
Here's an example of what you are looking:
=if(INDEX(FILTER(OFFSET(D2,,,1,COLUMNS(1:1)-column(D2)+1),OFFSET(D2,,,1,COLUMNS(1:1)-column(D2)+1)<>""),1)<=B2,"yes","")
this part of formula:
OFFSET(D2,,,1,COLUMNS(1:1)-column(D2)+1)
returns the range starting from given cell (D2) to the end of Sheet (COLUMNS(1:1)-column(D2)+1)
ArrayFormula
I suggest using ArrayFormula, it'll expand automatically:
=ARRAYFORMULA(if(REGEXEXTRACT(SUBSTITUTE(trim(transpose(query(transpose(OFFSET(D2,,,COUNTA(A2:A),COLUMNS(1:1)-column(D2)+1)),,COLUMNS(OFFSET(D2,,,COUNTA(A2:A),COLUMNS(1:1)-column(D2)+1)))))," ",", "),"\d+")*1<=OFFSET(B2,,,COUNTA(A2:A)),"yes",""))
It assumes that 'Item' column has no blank values.
The solution from #Max Makhrov works, and has the advantage of using a single formula for the whole column.
However, it assumes that all of your columns at the right from your ready column (D) will be need_ columns.
The solution from #dmusgrave also works, provided you remove the extra "=" before INDEX:
=IF(INDEX(FILTER(D2:Z2,D2:Z2<>""),1)<=B2,"Yes","").
However, it makes the same assumption, and also limits at column Z.
Such assumptions seem reasonable, but if they are limiting you, here's how you can have any number of need_ columns starting right of your ready column:
=IF(INDEX(FILTER(INDIRECT( "D"&ROW()&":"&CHAR(67+COLUMNS(FILTER($1:$1,LEFT($1:$1, 4)="need")))&row() ), INDIRECT( "D"&ROW()&":"&CHAR(67+COLUMNS(FILTER($1:$1,LEFT($1:$1,4)="need")))&row() )<>""),1)<=B2,"Yes","")
The idea is simply to replace D2:Z2 (in #dmusgrave's solution) by :
INDIRECT( "D"&ROW()&":"&CHAR(67+COLUMNS(FILTER($1:$1,LEFT($1:$1, 4)="need")))&row() )
Explanation: You start from D at current row, and you go until the last need_ column on the same current row.
CHAR(68) is D, to which you add the number of columns titled need.*, minus one (hence the 67).
Using the same logic, you can easily make your formula more robust/generic, such as not having the need_ columns starting right form the ready column, etc.
I have 5 columns of numbers that I want to sort per row into another set of columns. I figured I need to use small() (e.g. small(a2:e2,1) for f2; small(a2:e2,2) for g2 and so on). Is there away to iterate this for the next rows; if possible using only native google spreadsheet formulas?
Thanks in advance
I was able to make a temporary work around, but I had to use 3 cheat columns. It looks ok for now but I imagine it will be troublesome for really huge numbers.
Here's a sample sheet for reference: https://docs.google.com/spreadsheets/d/1MQTP2XkRsPRAnPQ5wLhkR8JoNVY6YOExVlOkkX8UeRs/edit#gid=0
The original data are in A3:E
The first cheat column (G3:G) simply creates a column of numbers from 1 to the largest number found in the source data. 1-9 is changed to 01-09 for easier searching. "#" is then added at the end-this will come handy later:
Cheat Column 1 =filter(if(row(A:A)=max(A:E)+1,ʺ#ʺ,text(row(A:A),ʺ00ʺ)),row(A:A)<=max(A:E)+1)
The second cheat column (H3:H) combines each row into a string separated by "-" with a "#" marker:
Cheat Column 2=filter(text(A3:A,ʺ00ʺ)&ʺ-ʺ&text(B3:B,ʺ00ʺ)&ʺ-ʺ&text(C3:C,ʺ00ʺ)&ʺ-ʺ&text(D3:D,ʺ00ʺ)&ʺ-ʺ&text(E3:E,ʺ00ʺ)&ʺ#ʺ,A3:A<>ʺʺ)
The last cheat column (I3:I) sorts each line (from cheat column 2) by finding each number from cheat column from 01 up to the max number, then the "#" char (this ensures that each line will still have the # end marker). "Find" will return the "position" of each number or an error if it's not found. By using "if", we can make "find" return the actual number or "" instead.
=filter(arrayformula(if(iferror(find(transpose(filter(G3:G,G3:G<>ʺʺ)),H3:H),ʺʺ), transpose(filter(G3:G,G3:G<>ʺʺ)),ʺʺ)),A3:A<>ʺʺ)
The formula above creates as many columns as there are numbers from cheat column 1. To prevent this, a "-" is added to each number then "Concatenate" is used to combine everything into one massive string with each set separated by "#". The string is then split using the "#" marker.
Cheat Column 3 =transpose(split(concatenate(filter(arrayformula(if(iferror(find(transpose(filter(G3:G,G3:G<>ʺʺ)),H3:H),ʺʺ),ʺ-ʺ&transpose(filter(G3:G,G3:G<>ʺʺ)),ʺʺ)),A3:A<>ʺʺ)),ʺ#ʺ))
Each number is then separated into each corresponding column by using mid().
Small 1 =filter(mid(I3:I,2,2)*1,A3:A<>ʺʺ)
Small 2 =filter(mid(I3:I,5,2)*1,A3:A<>ʺʺ)
Small 3 =filter(mid(I3:I,8,2)*1,A3:A<>ʺʺ)
Small 4 =filter(mid(I3:I,11,2)*1,A3:A<>ʺʺ)
Small 5 =filter(mid(I3:I,14,2)*1,A3:A<>ʺʺ)
Note that the formula above is only for numbers 1-99. For larger numbers, the Text() formulas should have more zeroes to correspond to the number of digits of the biggest number. The Mid() formulas should also be adjusted accordingly.
I would like to stress that I am very far from being a spreadsheet expert and that this solution is very "unoptimized". It requires several cheat columns; with the first one even having more rows than the original data. If anyone can help me get rid of the cheat columns (or at least the first one) I will be very grateful.
How about using SMALL like you mentioned in your question?
=small($A3:$E3,column()-columns($A3:$G3))
You will need to change the ranges accordingly. The last $G$3 is the cell just before the cell where the formula is placed.
Sample