I have a database of about 100 columns with similar data to from COL A to COL H.
I use the formula in COL J to search in a column for two consecutive rows with "-" and mark the second row as a double as you can see on J16 and J32.
This method is time consuming because I do often search for different columns and have to change the formula each time.
I would like something like N3. Entering the column ID and when I hit enter I will get automatically the count of rows with two consecutive "-" and also I would like to increase to search for triples and quadruples.
Any help will be appreciate.
Formula on J2:
=IF(AND(OR(F2=F1,F1="-"),F2="-"),"double","")
image here
In N5 to count doubles,
=COUNTIFS(INDEX(A:H, 2, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64)), CODE(UPPER(N3))-64), "-",
INDEX(A:H, 3, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64))+1, CODE(UPPER(N3))-64), "-")
This is the dynamic equivalent of using,
=COUNTIFS(G2:G20, "-", G3:G21, "-")
In N6 to count triples,
=COUNTIFS(INDEX(A:H, 2, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64)), CODE(UPPER(N3))-64), "-",
INDEX(A:H, 3, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64))+1, CODE(UPPER(N3))-64), "-",
INDEX(A:H, 4, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64))+2, CODE(UPPER(N3))-64), "-")
In N7 to count quads,
=COUNTIFS(INDEX(A:H, 2, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64)), CODE(UPPER(N3))-64), "-",
INDEX(A:H, 3, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64))+1, CODE(UPPER(N3))-64), "-",
INDEX(A:H, 4, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64))+2, CODE(UPPER(N3))-64), "-",
INDEX(A:H, 5, CODE(UPPER(N3))-64):INDEX(A:H, MATCH("zzz", INDEX(A:H, , CODE(UPPER(N3))-64))+3, CODE(UPPER(N3))-64), "-")
If you require quints, you should be able to get the idea from those.
You want to use your column entry in cell N3. You can do this using the indirect function. Just change the formula in cell J2 from this:
=IF(AND(OR(F2=F1,F1="-"),F2="-"),"double","")
...to this:
=IF(AND(INDIRECT(N$3&ROW())="-",INDIRECT(N$3&ROW()-1)="-"),"double","")
You can catch triples and quadruples in the same way, try this formula ...it'll only work from row 4 onwards, and the results may feel messy, depending on what you need:
=IF(AND(INDIRECT(N$3&ROW()-1)="-",INDIRECT(N$3&ROW())="-"),IF(AND(INDIRECT(N$3&ROW()-2)="-",INDIRECT(N$3&ROW()-1)="-",INDIRECT(N$3&ROW())="-"),IF(AND(INDIRECT(N$3&ROW()-3)="-",INDIRECT(N$3&ROW()-2)="-",INDIRECT(N$3&ROW()-1)="-",INDIRECT(N$3&ROW())="-"),"quadruple","triple"),"double"),"")
With reference to the figure at the bottom, there are:
Helper cells N1:N2 and N9:N19, whose contents helps the formulas you need being more concise.
See below the explanation and formulas for these.
Cells with the formulas you need, using SUMPRODUCT combined with some form of dynamic referencing.
Cells N5:N7
give the result you want, but with fixed references. These are
N5: =SUMPRODUCT(($G$2:$G$21="-")*($G$3:$G$22="-"))
N6: =SUMPRODUCT(($G$2:$G$20="-")*($G$3:$G$21="-")*($G$4:$G$22="-"))
N7: =SUMPRODUCT(($G$2:$G$19="-")*($G$3:$G$20="-")*($G$4:$G$21="-")*($G$5:$G$22="-"))
You can grasp the systematics.
Cells O5:O7
give the same result, using INDIRECT instead of fixed references (option #1 for what you need, see this). These are
O5: =SUMPRODUCT(
(INDIRECT($N$3&$N$9):INDIRECT($N$3&($N$10-1))="-")
*(INDIRECT($N$3&($N$9+1)):INDIRECT($N$3&$N$10)="-")
)
O6: =SUMPRODUCT(
(INDIRECT($N$3&$N$9):INDIRECT($N$3&($N$10-2))="-")
*(INDIRECT($N$3&($N$9+1)):INDIRECT($N$3&($N$10-1))="-")
*(INDIRECT($N$3&($N$9+2)):INDIRECT($N$3&$N$10)="-")
)
You can grasp the systematics and write the formula for cell O7.
Cells P5:P7
give the same result, using OFFSET instead of fixed references (option #2 for what you need, see this, this, or this). These are
P5: =SUMPRODUCT(
(OFFSET($A$1,$N$12,$N$14):OFFSET($A$1,$N$13-1,$N$14)="-")
*(OFFSET($A$1,$N$12+1,$N$14):OFFSET($A$1,$N$13,$N$14)="-")
)
P6: =SUMPRODUCT(
(OFFSET($A$1,$N$12,$N$14):OFFSET($A$1,$N$13-2,$N$14)="-")
*(OFFSET($A$1,$N$12+1,$N$14):OFFSET($A$1,$N$13-1,$N$14)="-")
*(OFFSET($A$1,$N$12+2,$N$14):OFFSET($A$1,$N$13,$N$14)="-")
)
You can grasp the systematics and write the formula for cell P7.
There are likely other options combining INDIRECT and OFFSET (see this). An option using INDEX (although likely not the only variant) was covered by Jeeped.
Note on helper cells:
I suggest having helper cells, and this applies to other answers posted here as well.
Of course you may move these cells around, adjusting the corresponding formulas.
The only non trivial formula here is for cell N11, =COLUMN(INDIRECT($N$3&"1")) (see this or this).
Cell N19 may be useful if you are going to use INDEX.
Related
I have an excel table for raw material offloads. All get tested, but some don't get offloaded right away. I'm trying to create a formula that looks at the future 20 entries for the same railcar and see if it changed from "N" to "Y" for offload.
Here's what my data looks like:
CAR # Offloaded?
CTCX733450 N
CTCX733450 Y
GATX207935 N
CTCX733472 Y
GATX207923 N
GATX207935 Y
GATX207923 Y
I've tried COUNTIF functions and IF functions. I can detect the duplicate railcars, but can't correspond the Y and N with the railcar.
Any help is appreciated.
You can use COUNTIFS to check multiple columns at once. For example
=COUNTIFS(A3:A22, A2, B3:B22, "Y")
This will take the value in cell A2 (CTCX733450), then look at the following 20 rows (Rows 3-22) to see how many time Column A is that value and Column B is "Y". If it is greater than 0, then one of the next 20 instances of that Railcar has been Offloaded.
Notably, this "the next 20 rows", and not "the next 20 entries for the same railcar". For that, we would need to use AGGREGATE and INDEX to find the 20th time that railcar next appears, which will be the final row we check.
For the time being, we will substitute for this row value with ROW_VALUE. This then lets us rewrite our formula using INDEX, as follows:
=COUNTIFS(A3:INDEX(A:A, ROW_VALUE), A2, B3:INDEX(B:B, ROW_VALUE), "Y")
Simple enough! The tricky bit, though, is now working out what value we should have for ROW_VALUE. This is where the AGGREGATE comes in.
You see, we can use AGGREGATE to get the kth (fourth parameter) smallest (First parameter = 15) non-error value (Second parameter = 6) from a list of values (third parameter). We can also make a list of Rows where column A is the same as the value in A2, by using #DIV0! (divide by zero) errors, and the fact that TRUE/FALSE can be treated as 1/0
AGGREGATE(15, 6, Row(A:A)/(A:A=A2), k)
In your case, we want k to be 20 + how many copies of railcar we already have. We can count how many copies of the railcar have passed us by using COUNTIF, so long as we lock one end to the first row:
AGGREGATE(15, 6, Row(A:A)/(A:A=A2), 20+COUNTIF(A$1:A2, A2))
Now, in theory we could shove that in as our ROW_VALUE. In practice, I can immediately see 2 big problems with it. The first, working on Whole Columns is slow. Second, and more important: What happens if there are less than 20 copies of the railcar remaining? You get a #NUM! error, that's what.
We can fix both of these issues with COUNTA (assuming that there are no rows without railcar numbers). For the first one, we will use INDEX again:
AGGREGATE(15, 6, Row(A$1:INDEX(A:A, COUNTA(A:A)))/(A$1:INDEX(A:A, COUNTA(A:A))=A2), 20+COUNTIF(A$1:A2, A2))
Alternatively, you can rearrange this to get rid of the COUNTIF at the end, by starting your Range on the next row, and just looking for the 20th number:
AGGREGATE(15, 6, Row(A3:INDEX(A:A, COUNTA(A:A)))/(A3:INDEX(A:A, COUNTA(A:A))=A2), 20)
For the second issue, we'll use IFERROR. This is a simple function - it just says "Return this value, unless it is an error - then, use this other value instead". Our "other value" will be the COUNTA of Column A, which should give us the last row in your list of Railcars:
IFERROR(AGGREGATE(15, 6, Row(A3:INDEX(A:A, COUNTA(A:A)))/(A3:INDEX(A:A, COUNTA(A:A))=A2), 20),COUNTA(A:A))
This then gives us our ROW_VALUE, which we can plug into our other earlier COUNTIFS:
=COUNTIFS(A3:INDEX(A:A, IFERROR(AGGREGATE(15, 6, Row(A3:INDEX(A:A, COUNTA(A:A)))/(A3:INDEX(A:A, COUNTA(A:A))=A2), 20),COUNTA(A:A))), A2, B3:INDEX(B:B, IFERROR(AGGREGATE(15, 6, Row(A3:INDEX(A:A, COUNTA(A:A)))/(A3:INDEX(A:A, COUNTA(A:A))=A2), 20),COUNTA(A:A))), "Y")
Finally, and optionally: we can make a slight boost in calculation time by working out if the AGGREGATE will error before it does so, by checking if there are at least 20 more entries for the Railcar. This also replaces the IFERROR with an IF statement, but makes the whole equation longer:
=COUNTIFS(A3:INDEX(A:A, IF(COUNTIF(A3:INDEX(A:A, COUNTA(A:A)),A2)<20, COUNTA(A:A), AGGREGATE(15, 6, Row(A3:INDEX(A:A, COUNTA(A:A)))/(A3:INDEX(A:A, COUNTA(A:A))=A2), 20))), A2, B3:INDEX(B:B, IF(COUNTIF(A3:INDEX(A:A, COUNTA(A:A)),A2)<20, COUNTA(A:A), AGGREGATE(15, 6, Row(A3:INDEX(A:A, COUNTA(A:A)))/(A3:INDEX(A:A, COUNTA(A:A))=A2), 20))), "Y")
We have replaced this ROW_VALUE
IFERROR(AGGREGATE(15, 6, Row(A3:INDEX(A:A, COUNTA(A:A)))/(A3:INDEX(A:A, COUNTA(A:A))=A2), 20),COUNTA(A:A))
with this one instead
IF(COUNTIF(A3:INDEX(A:A, COUNTA(A:A)),A2)<20, COUNTA(A:A), AGGREGATE(15, 6, Row(A3:INDEX(A:A, COUNTA(A:A)))/(A3:INDEX(A:A, COUNTA(A:A))=A2), 20))
If we do not use VBA, any methods can be used to split the following cell in excel?
Please advise the methods for splitting text "ParisFrancePeter" to 3 separate words "Paris" "France" and "Peter".
'first word
=REPLACE(A1, AGGREGATE(15, 7, ROW(2:99)/(CODE(MID(A1, ROW(2:99), 1))<=90), 1), LEN(A1), "")
'middle word
=MID(A1, AGGREGATE(15, 7, ROW(2:99)/(CODE(MID(A1, ROW(2:99), 1))<=90), 1), LEN(A1)-AGGREGATE(14, 7, ROW(2:99)/(CODE(MID(A1, ROW(2:99), 1))<=90), 1)+2)
'last word
=REPLACE(A1, 1, AGGREGATE(14, 7, ROW(2:99)/(CODE(MID(A1, ROW(2:99), 1))<=90), 1)-1, "")
If you also have other than three words to split, then, with your original string in A1, enter the formula below in B1 and fill right as far as required:
=IFERROR(MID($A1,IFERROR(AGGREGATE(15,6,1/((CODE(MID($A1,seq,1))>=65)*(CODE(MID($A1,seq,1))<=90))*(seq),COLUMNS($A:A)),""),IF(IFERROR(AGGREGATE(15,6,1/((CODE(MID($A1,seq,1))>=65)*(CODE(MID($A1,seq,1))<=90))*(seq),COLUMNS($A:B)),"")="",99,IFERROR(AGGREGATE(15,6,1/((CODE(MID($A1,seq,1))>=65)*(CODE(MID($A1,seq,1))<=90))*(seq),COLUMNS($A:B)),"")-IFERROR(AGGREGATE(15,6,1/((CODE(MID($A1,seq,1))>=65)*(CODE(MID($A1,seq,1))<=90))*(seq),COLUMNS($A:A)),""))),"")
where seq is a Named Formula that generates an array of numbers {1...255} and refers to:
=ROW(INDEX($1:$65535,1,1):INDEX($1:$65535,255,1))
The code will return the starting point of each Upper Case letter, and then uses the MID function to return the substring between that and the next upper case letter. Then there is some error checking.
So in the example above, I would like to extract all cells that contain "A" as the first letter(and length=9, though it does not matter). Now I am able to run the function for one cell, but I want to run it as an array formula so that I do not have to drag down 1000 cells every time. Below is my code:
=IFERROR(INDEX($A$1:$A$3, IF(AND(LEFT(A2,1)="A", LEN(A2)=9), ROW($A$1:$A$3),"")),"")
The problem here is that when I enter the code with "Ctrl + Shift + Enter", the criteria would be only confined to A2, which is the cell address I manually entered. Is there anyway to check for every single cell without having to drag down WITHOUT USING VBA? I know using VBA would make it a lot easier, but I just want to understand the basics of Excel further.
Try,
=iferror(index(a:a, aggregate(15, 7, row(a:a)/(left(a$1:index(a:a, match("zzz", a:a)))="a"), row(1:1))), text(,))
'with 9 length criteria
=iferror(index(a:a, aggregate(15, 7, row(a:a)/((left(a$1:index(a:a, match("zzz", a:a)))="a")*(len(a$1:index(a:a, match("zzz", a:a)))=9)), row(1:1))), text(,))
Fill down as necessary.
If you only want the single left-most character from a string of text, you do not have to supply a 1; you can omit the number of characters argument.
I need a little help, I have an Excel book with a long list of customers and in the adjacent cell is ether a 1 or 0 depending if the customer has ordered in the past month.
How can I split this list into 2 lists on another page one been ordered in the past month and the other not ordered,
Im unable to use Macros due to security levels on the PC.
Thanks
Try,
'for ones
=INDEX(A:A, AGGREGATE(15, 7, ROW(B$2:INDEX(B:B, MATCH(1E+99, B:B)))/(B$2:INDEX(B:B, MATCH(1E+99, B:B))=1), ROW(1:1)))
'for zeroes
=INDEX(A:A, AGGREGATE(15, 7, ROW(B$2:INDEX(B:B, MATCH(1E+99, B:B)))/(B$2:INDEX(B:B, MATCH(1E+99, B:B))=0), ROW(1:1)))
Of course, you will have to modify to suit the different worksheet.
See the image below. Using array formulas (Ctrl+Shift+Enter) I entered this into D2:D8:
=IFERROR(INDEX(A2:A8,SMALL(IF(B2:B8,ROW(A2:A8)-ROW(A1)),ROW(A2:A8)-ROW(A1))),"")
and this into E2:E8:
=IFERROR(INDEX(A2:A8,SMALL(IF(1-B2:B8,ROW(A2:A8)-ROW(A1)),ROW(A2:A8)-ROW(A1))),"")
Explanation
This is how Excel will calculate the first formula:
=IFERROR(INDEX(A2:A8,SMALL(IF(B2:B8,ROW(A2:A8)-ROW(A1)),ROW(A2:A8)-ROW(A1))),"")
=IFERROR(INDEX(A2:A8,SMALL(IF({1;0;1;0;0;0;1},{2;3;4;5;6;7;8}-1),{2;3;4;5;6;7;8}-1)),"")
=IFERROR(INDEX(A2:A8,SMALL(IF({1;0;1;0;0;0;1},{1;2;3;4;5;6;7}),{1;2;3;4;5;6;7})),"")
=IFERROR(INDEX(A2:A8,SMALL({1;FALSE;3;FALSE;FALSE;FALSE;7},{1;2;3;4;5;6;7})),"")
Since the second argument of SMALL is an array, it will find the 1st, 2nd, 3rd, etc. value, ignoring FALSE (it will return #NUM! for the 4th through 7th value).
=IFERROR(INDEX(A2:A8,{1;3;7;#NUM!;#NUM!;#NUM!;#NUM!}),"")
=IFERROR({"A";"C";"G";#NUM!;#NUM!;#NUM!;#NUM!},"")
={"A";"C";"G";"";"";"";""}
I need to return a cell that has text in it, but am running into difficulty.
Above is a sample table I'm working with. What I'd like to be able to do, is lookup id 1 and have it output Rich. When I do a vlookup, however, it gives no output. And while vlookup min/max will output integers, they don't work with text. Does anyone know how I can scan multiple ids, but only output the filled text cell?
There may be a shorter formula for this but I banged this off quickly and it does dynamically truncate the ranges in column B down to the minimum number of rows necessary.
=INDEX(B:B, AGGREGATE(15, 6, ROW(B2:INDEX(B:B, MATCH("zzz",B:B )))/(ISTEXT(B2:INDEX(B:B, MATCH("zzz",B:B )))*(A2:INDEX(A:A, MATCH("zzz",B:B )))=D3), 1))
To retrieve a second, third, etc. entry change the k parameter of AGGREGATE to a COUNTIF and fill down.
=INDEX(B:B, AGGREGATE(15, 6, ROW(B$2:INDEX(B:B, MATCH("zzz",B:B )))/(ISTEXT(B$2:INDEX(B:B, MATCH("zzz",B:B )))*(A$2:INDEX(A:A, MATCH("zzz",B:B )))=D3), COUNTIF(D$3:D3, D3)))