Is there someone who can help me to break down this long formula and ,by examining the independent elements, help us to understand how it works?
It takes two contiguous columns, in this case A and B.
First a value from A is taken.
If there is a next value down A, it will repeat the actual value until that row.
If there are no values left in A, it returns "" (blank)
So, in "B1" we write this very long formula:
ArrayFormula(IF(ROW(A:A)<=MATCH(2;1/(A:A<>"");1);LOOKUP(ROW(A:A);ROW(A:A)/IF(A:A<>"";TRUE;FALSE);A:A);))
The result is like this:
A
B
foo
foo (here goes the formula)
foo
bar
bar
bar
bar
qux
qux
(blank)
(blank)
(blank)
(blank cells continue till the end of the file, because col A ends with "qux")
This array is quite useful, but I guess if there is a cleaner formula, or a better approach to this task...
I suspect that this formula is a workaround and Spreadsheets must have an elegant resource to get done this kind of task.
Try this:
=ARRAYFORMULA(
IF(
ROW(A:A) > MATCH(2, 1 / (A:A <> "")),,
VLOOKUP(ROW(A:A), FILTER({ROW(A:A), A:A}, A:A <> ""), 2)
)
)
MATCH(2, 1 / (A:A <> "")) gives you the row number of the last non-empty cell in a column A:A.
Or if there might be blank cells at the top, then use additional condition:
=ARRAYFORMULA(
IF(
(ROW(A:A) > MATCH(2, 1 / (A:A <> "")))
+ (ROW(A:A) < MATCH("*", A:A,)),,
VLOOKUP(ROW(A:A), FILTER({ROW(A:A), A:A}, A:A <> ""), 2)
)
)
use:
=ARRAYFORMULA(IF(B:B="",, VLOOKUP(ROW(A:A), IF(A:A<>"", {ROW(A:A), A:A}), 2, 1)))
Related
I'm wondering how to count a checkbox in a row if a condition is checked, in this case the "Si" in column B, which I translate to "TRUE" in Column D ( would later hide ). It should be fairly easy but can't find a solution.
Everything I tried works only if every single B/D has a Si/True, which is not what I want, I want to count each row that has a checkbox checked that doesn't have a SI ( or that does, since it's the same process )
try:
=INDEX(SUM(IF(JUEVES!B2:B="si"; 1; 0)))
or:
=SUMPRODDUCT(JUEVES!B2:B="si")
if those are two conditions (not clear from your question):
try:
=INDEX(SUM(IF((JUEVES!B2:B="si")*(JUEVES!D2:D=TRUE); 1; 0)))
I have a Google Sheet that is populated automatically via Zapier integration. For each new row added, I need to evaluate a given cell (Shipper Name) to find last instance of Shipper Name in prior rows, and if so, return Row# for the last entry.
Example Data Sheet
I am trying to create a formula that simply looks at name in new row and returns the number of the most recent row with that name.
Formula needs to run as an Array formula so that the data auto populates with each new row added to the Sheet.
I have tried to use this formula, but when refactored as Array formula, it doesn't populate new values for new rows, it just repeats the first value for all rows.
From Row J:
=sumproduct(max(row(A$1:A3)*(F4=F$1:F3)))
I need this formula refactored to be an Array formula that auto populates all the cells below it.
I have tried this version, but it doesn't work:
=ArrayFormula(IF(ISBLANK($A2:$A),"",sumproduct(max(row(A$1:A3)*($F4:$F=F$1:F3))))
A script (custom function maybe?) would be better.
Solution 1
Below is a formula you can place into the header (put in in J1, remove everything below).
It works much faster than the second solution and has no N² size restriction. Also it works with empty shippers (& "♥" is for those empty ones): as long as A:A column has some value it will not be ignored.
={
"Row of Last Entry";
ARRAYFORMULA(
IF(
A2:A = "",
"",
VLOOKUP(
ROW(F2:F)
+ VLOOKUP(
F2:F & "♥",
{
UNIQUE(F2:F & "♥"),
SEQUENCE(ROWS(UNIQUE(F2:F)))
* POWER(10, INT(LOG10(ROWS(F:F))) + 1)
},
2,
0
),
SORT(
{
ROW(F2:F) + 1
+ VLOOKUP(
F2:F & "♥",
{
UNIQUE(F2:F & "♥"),
SEQUENCE(ROWS(UNIQUE(F2:F)))
* POWER(10, INT(LOG10(ROWS(F:F))) + 1)
},
2,
0
),
ROW(F2:F);
{
SEQUENCE(ROWS(UNIQUE(F2:F)))
* POWER(10, INT(LOG10(ROWS(F:F))) + 1),
SEQUENCE(ROWS(UNIQUE(F2:F)), 1, 0, 0)
}
},
1,
1
),
2,
1
)
)
)
}
Details on how it works
For every row we use VLOOKUP to search for a special number in a sorted virtual range to get the row number of the previous entry matching current.
A special number for a row is constructed like this: we get a sequential number for the current entry among unique entries and append to it current row number.
The right part (row number) of the resulting special numbers must be aligned between them. If the entry has sequential number 13 and the row number is 1234 and there are 100500 rows, then the number must be 13001234. 001234 is the aligned right part.
Alignment is done by multiplying a sequential number by 10 to the power of (log10(total number of rows) + 1), gives us 13000000 (from the example above). This approach is used to avoid using LEN and TEXT - working with numbers is faster then working with strings.
Virtual range has almost the same special numbers in the first column and original row numbers in the second.
Almost the same special numbers: they just increased by 1, so VLOOKUP will stop at most one step before the number corresponding to the current string.
Also virtual range has some special rows (added at the bottom before sorting) which have all 0's as the right part of their special numbers (1st column) and 0 for the row number (2nd column). That is done so VLOOKUP will find it for the first occurrence of the entry.
Virtual range is sorted, so we could use is_sorted parameter of the outer VLOOKUP set to 1: that will result in the last match that is less or equal to the number being looked for.
& "♥" are appended to the entries, so that empty entries also will be found by VLOOKUP.
Solution 2 - slow and has restrictions
But for some small enough number of rows this formula works (put in in J1, remove everything below):
={
"Row of Last Entry";
ARRAYFORMULA(
REGEXEXTRACT(
TRANSPOSE(QUERY(TRANSPOSE(
IF(
(FILTER(ROW(F2:F), F2:F <> "") > TRANSPOSE(FILTER(ROW(F2:F), F2:F <> "")))
* (FILTER(F2:F, F2:F <> "") = TRANSPOSE(FILTER(F2:F, F2:F <> ""))),
TRANSPOSE(FILTER(ROW(F2:F), F2:F <> "")),
""
)
), "", ROWS(FILTER(F2:F, F2:F <> "")))),
"(\d*)\s*$"
)
)
}
But there is a problem. The virtual range inside of the formula is of size N², where N is the number of rows. For current 1253 rows it works. But there is a limit after which it will throw an error of a range being too large.
That is the reason to use FILTER(...) and not just F2:F.
Here is a significantly simpler way to get at the information you're interested in. (I think.) I'm mostly guessing about what you want because your question wasn't really about what you want, but rather about how to get something that you think would help you get what you want. This is an example of an XY problem. I attempted to guess based on experience at what you're really after.
This editable sheet contains just 3 formulas. 2 on the raw data sheet and one in a new tab called "analysis."
The first formula on the Raw data tab extracts a properly formatted timestamp using a combination of MMULT and SPLIT functions and looks like this:
=ARRAYFORMulA({"Good Timestamp";IF(A2:A="",,MMULT(N(IFERROR(SPLIT(A2:A,"T"))),{1;1}))})
The second formula finds the amount of time since the previous timestamp for that Shipper. and subtracts it from the current timestamp thereby giving you the time between timestamps. However, it only does this if the time is less than 200 minutes. IF it is more than 200 minutes, it assumes that was a different shift for that shipper. It looks like this and uses a combination of LOOKUP() and SUBSTITUTE() to make sure it's pulling the correct timestamps. Obviously, you can find and change the 200 value to something more appropriate if it makes sense.
=ARRAYFORMULA({"Minutes/Order";IF(A2:A="",,IF(IFERROR((G2:G-1*SUBSTITUTE(LOOKUP(F2:F&G2:G-0.00001,SORT(F2:F&G2:G)),F2:F,""))*24*60)>200,,IFERROR((G2:G-1*SUBSTITUTE(LOOKUP(F2:F&G2:G-0.00001,SORT(F2:F&G2:G)),F2:F,""))*(24*60))))})
The third formula, on the tab called analysis uses query to show the average minutes per order and the number of orders per hour that each shipper is processing. It looks like this:
=QUERY({'Sample Data'!F:I},"Select Col1,AVG(Col3),COUNT(Col3)/(SUM(Col3)/60) where Col3 is not null group by Col1 label COUNT(Col3)/(SUM(Col3)/60)'Orders/ hour',AVG(Col3)'Minutes/ Order'")
Hopefully I've guessed correctly at your real goals. Always do your best to explain what they are rather than asking for only a small portion that you think will help you get to the answer. You can end up overcomplicating your process without realizing it.
Thank you for your time!
I am trying to make a trade journal in Google Spreadsheets.
What I want is the entry price and exit price of the trades,
Which are cell C10 and cell C11 in the screenshot image below.
I just manually typed the correct value - 960 and 2200.
Fortunately, the entry price for C10 is always what's in cell H2,
Because the first input will always be "Buy" in column F.
However I'm stuck finding the exit value.
I want it to find the last non-zero value of column H, only when column F contains "Sell".
What formula can I write?
enter image description here
You can do it with a combination of
INDEX
QUERY
COUNTA
Query for entries in column H where F is equal to "sell" and H is not 0.
From the retrieved subset of data, get the one with the last index (by counting the total amount of indices with COUNTA).
Sample formula:
=INDEX(
QUERY(F2:H, "select H where (F = 'sell' and H <> 0)"),
COUNTA(
QUERY(F2:H, "select H where (F = 'sell' and H <> 0)")
),
1
)
You can also do it without QUERY() (even though it is a really cool function).
=INDEX(H:H,MAX(IF((F:F="Sell")*H:H,ROW(F:F)),-1))
Find last row number where "Sell" appears in Column F and the corresponding value in Column H is non-zero. Since the row number monotonically increases, MAX() always gives us the last occurrence. If the row says "Buy" instead, or the value is 0, then IF() returns -1. We cannot use 0 because INDEX() evidently returns the whole range if we do that.
Use that row number as an index for column H to get the corresponding value. If no valid value was found, we get a #NUM error, which you can handle with IFERROR() if you'd like.
Note: we can't use a VLOOKUP() and approximate match because Action is unsorted.
I am working on a formula that will yield the following result:
- Checks value of cells (B2:B) and returns the value of cells (A2:A) if the value is less than or equal to 'x'.
See this image for reference.
The expected outcome here would be:
- E2: Raking leaves, Shoveling Dirt
- E3: Doing Laundry
- E4: Changing Spark Plugs
I can get the formula working on a basic level (per row) with this:
=IF(B2:B = 1,A2:A ,null)
I don't know how to go about comma-separating the titles in ONE cell like I've put in my example though. Can anyone help?
You can filter for the criterion
FILTER(A2:A, B2:B <= 1)
If you want them all in one cell you can join the resulting range together like this
=JOIN(", ", FILTER(A2:A, B2:B <= 1))
Given a spreadsheet with two columns, say A and B, each containing n values under it, all text; is there a formula that allows me to fill just one cell containing the amount of equal values in columns A and B?
Example:
A B
-----
1 M M
2 L M
3 L L
4 M M
5 M L
-----
3
Since columns A and B both contain an M in rows 1 and 4, and an L in row 3, the result is (i.e. 2+1).
A simple solution is to use QUERY function in google spreadsheet:
=SUM(QUERY(A1:B5, "Select 1 where A = B"))
Or using SUMPRODUCT:
=ARRAYFORMULA(SUM(((A:A)=(B:B)) * (1) ))
One of possible solution will be to add the following formula in column C: =N(EXACT(A1,B1)),
copy it throughout the column down to the last row and then sum up the column C values using =SUM(C1:C5).
Here we go:
=IF(EQ(LEFT(A0, 1), "A"),
SUM(ARRAYFORMULA(N(EXACT(TRANSPOSE(A1:A5), TRANSPOSE(B1:B5))))),
"")
Reading: if the value in row 0 (it doesn't exist, but my example above does ;) ) is equal to the text "A", take the sum of an array N, otherwise put in an empty string. ("")
Array N is build by taking the transpose of columns A and B. (Turning them, so they look like a row) and comparing the values. (Burnash gave me the options "N" and "EXACT") The formula N transforms this into a 1 or 0.
Copy paste the formula in an entire row and what do you know... It worked! That was hellish for something so trivial.
Thanks anyway.