Compare last two values in a selection [Excel] - excel-formula

I made a formula to compare the values of the last two non-empty cells in a range (M48-X48), and according to the difference between the values to give me an output (u, p, or q). However, if the range has an empty cell between the last two values the formula doesn't work anymore, because I simply subtract 1 cell from the index of the last value, instead of going back to the last non-empty cell. How can I add this feature to the formula?
=IF((INDEX(M48:X48;MATCH(9E+99+307;M48:X48))-INDEX(M48:X48;MATCH(9E+99+307;M48:X48)-1))=0;"u";IF(INDEX(M48:X48;MATCH(9E+99+307;M48:X48))-INDEX(M48:X48;MATCH(9E+99+307;M48:X48)-1)>0;"p";"q"))
Example the way it works now:
M: 1; N: 2; O: 3; rest until X is empty
Output = p
Example raneg which causes an error:
M: 1; N: empty; O: 3; rest until X is empty

Can't you just do a find and replace, blanks with zero's in your data range?
You could drop IF statements into your index/match but the longer they get, the longer they take to calculate in my experience and wondering if its worth the time investment. You tell me.

Using the AGGREGATE function the last to non-empty values can be easily extracted.
The solution to the question is the following
=IF(INDEX(M48:X48;AGGREGATE(14;6;COLUMN(M48:X48)/(M48:X48>0);1)-12)-INDEX(M48:X48;AGGREGATE(14;6;COLUMN(M48:X48)/(M48:X48>0);2)-12)=0;"u";IF(INDEX(M48:X48;AGGREGATE(14;6;COLUMN(M48:X48)/(M48:X48>0);1)-12)-INDEX(M48:X48;AGGREGATE(14;6;COLUMN(M48:X48)/(M48:X48>0);2)-12)>0;"p";"q"))
The component AGGREGATE(14;6;COLUMN(M48:X48)/(M48:X48>0);1) returns the column number of the last value, while the same component with a 2 instead of a 1 at the end, returns the column of the second last value. Using INDEX(component-12) the value of the corresponing cell is returned. The -12 is needed to account for the column shift because the range starts only at M. The rest of the formula stays unchanged from the way it was asked in the question.

Related

Numbering Based on Condition(s)

I'm trying to create auto numbering for Agents that are currently present and has numbers including zeroes 0 in 3rd or 4th column(zero meaning they don't get any stats but they are present)
Agents who has TEXT Value in the 3rd or 4th column are those who are not present (Ex: A = Absent, SL = Sick Leave, VL = Vacation Leave). Meaning, they should not be counted, therefore their value on 1st column should be blank, and therefore this should not stop the auto numbering for the rest of the agents below and should continue the count in sequence.
Can anyone help create formula that would fill the numbers on the 1st column automatically for those agents that are present and has value including 0 on column 3 or 4 (stats 1 or stats 2)?
To give more idea, I'm trying to show the current total number of agents who are currently present in this situation and will count their stats, and exclude all other agents who are not present and should not be counted.
Thank you!
Sequence Two Numeric Columns
Single Cell
In cell A3, a basic not spilling formula would be...
=IF(AND(ISNUMBER(C3),ISNUMBER(D3)),MAX(A$2:A2)+1,"")
... with the condition of a string in cell A2.
Without any conditions, you could try an improved version, similar to one of David Leal's suggestions:
=IF(AND(ISNUMBER(C3),ISNUMBER(D3)),
SUM(ISNUMBER(C$3:C3)*ISNUMBER(D$3:D3)),"")
Spill
In cell A3 you could use the following:
=LET(Data1,C3:C13,Data2,D3:D13,
Data,ISNUMBER(Data1)*ISNUMBER(Data2),
IFERROR(SCAN(0,Data,LAMBDA(a,b,a+b))/Data,""))
Line1: the inputs ('constants'), the same-sized single-column ranges
Line2: the zeros and ones, where the ones present the data of interest
Line3: the formula to replace the ones with the sequence and the zeros (errors due to division by zero) with an empty string
Converted to a LAMBDA, it could look like the following:
=LAMBDA(Data1,Data2,LET(
Data,ISNUMBER(Data1)*ISNUMBER(Data2),
IFERROR(SCAN(0,Data,LAMBDA(a,b,a+b))/Data,"")))(C3:C13,D3:D13)
Since it's such a long formula, you could create your own Lambda function by using this part...
=LAMBDA(Data1,Data2,LET(
Data,ISNUMBER(Data1)*ISNUMBER(Data2),
IFERROR(SCAN(0,Data,LAMBDA(a,b,a+b))/Data,"")))
... to define a name, e.g. SeqNumeric, when in the same cell, you could use
it simply with...
=SeqNumeric(C3:C13,D3:D13)
... instead.
Now you can use the function like any other Excel function anywhere in the workbook.
The Path
F3 =ISNUMBER(C3:C13)*ISNUMBER(D3:D13) - multiply: zeros-no, ones-yes
G3 =SCAN(0,F3#,LAMBDA(a,b,a+b)) - use the 'LAMBDA' helper function 'SCAN'
H3 =G3#/F3# - divide the 'scan' result by the zeros and ones
I3 =IFERROR(H3#,"") - convert the '#DIV/0!' errors to empty strings
The translation of the SCAN part could be something like the following:
Set the initial result a to 0.
Create a new array of the size of the initial array in F3#.
Loop through each element of the initial array, write its value to b, and replace a with the sum of a+b.
Write (the accumulated) a to the current element of the new array and repeat for the remaining elements of either array.
Return the new array.
Combine all of it in a LET.
J3 =LET(Data1,C3:C13,Data2,D3:D13,
Data,ISNUMBER(Data1)*ISNUMBER(Data2),
IFERROR(SCAN(0,Data,LAMBDA(a,b,a+b))/Data,""))
Convert to LAMBDA.
K3 =LAMBDA(Data1,Data2,LET(
Data,ISNUMBER(Data1)*ISNUMBER(Data2),
IFERROR(SCAN(0,Data,LAMBDA(a,b,a+b))/Data,"")))(C3:C13,D3:D13)
Copy the first part of the LAMBDA (note how it results in a #CALC! error since no parameters are supplied)...
L3 =LAMBDA(Data1,Data2,LET(
Data,ISNUMBER(Data1)*ISNUMBER(Data2),
IFERROR(SCAN(0,Data,LAMBDA(a,b,a+b))/Data,"")))
... and select Formulas -> Name Manager -> New to create your own function and finally use it with the following:
A3 =SeqNumeric(C3:C13,D3:D13)
You can try the following in cell A1:
=LET(B, B2:B12, C, C2:C12, f, 1*ISNUMBER(B*C),seq, SEQUENCE(ROWS(B)),
MAP(seq, LAMBDA(s, IF(INDEX(f,s)=0, "",SUM(FILTER(f, (seq<=s),0))))))
Here is the output:
A non-array version, expanding down the formula would be:
=IF(ISNUMBER(B2*C2), SUM(1*ISNUMBER(B$2:B2*C$2:C2)),"")
For the array version, it counts only if both columns Stat1 and Stat2 are numeric. The name f, has a value of 1 if the condition is TRUE, otherwise is 0. The MAP does the count if the index position of the f array is not zero, otherwise returns an empty string.
I think I got it.
This is the formula that I made
=IF(COUNTIFS(D2:BE2,"*",$D$1:$BE$1,TODAY())>0,"",MAX(A1:A$4)+1)
Countif criteria 1 = if the cell contains a letter and is counted > 0 then it will return blank, otherwise it will start the count using max function. The countif criteria 2 will will return the correct value according to the date today since the excel sheet has several data daily.

Is there a way to scan an entire column based on one cell in another column and pull out a value of the corresponding column?

A
B
C
D
4
1
6
5649
3
8
10
9853
5
2
7
1354
I have two worksheets, for example column A in sheet 1 and columns B-D in sheet 2.
What I want to do is to take one value in Column A, and scan both columns B and C and it is between those two values, then display the corresponding value from column D in a new worksheet.
There could be multiple matches for each of the cell in column A and if there is no match, to skip it and not have anything displayed. Is there a way to code this and somehow create a loop to do all of column A? I tried using this formula, but I think it only matches for each row and not how I want it to.
=IF(AND([PQ.xlsx]Sheet1!$A2>=[PQ.xlsx]Sheet2!$B2,[PQ.xlsx]Sheet1!$A2<[PQ.xlsx]Sheet2!$C2),[PQ.xlsx]Sheet2!$D$2,"")
How do I do this?
Thank you.
I'm not positive if I understood exactly what you intended. In this sheet, I have taken each value in A:A and checked to see if it was between any pair of values in B:C, and then returned each value from D:D where that is true. I did keep this all on a single tab for ease of demonstration, but you can easily change the references to match your own layout. I tested in Excel and then transferred to this Google Sheet, but the functions should work the same.
https://docs.google.com/spreadsheets/d/1-RR1UZC8-AVnRoj1h8JLbnXewmzyDQKuKU49Ef-1F1Y/edit#gid=0
=IFERROR(TRANSPOSE(FILTER($D$2:$D$15, ($A2>=$B$2:$B$15)*($A2<=$C$2:$C$15))), "")
So what I have done is FILTEREDed column D on the two conditions that Ax is >= B:B and <= C:C, then TRANSPOSED the result so that it lays out horizontally instead of vertically, and finally wrapped it in an error trap to avoid #CALC where there are no results returned.
I added some random data to test with. Let me know if this is what you were looking at, or if I misunderstood your intent.
SUPPORT FOR EXCEL VERSIONS WITHOUT DYNAMIC ARRAY FUNCTIONS
You can duplicate this effect with array functions in pre-dynamic array versions of Excel. This is an array function, so it has be finished with SHFT+ENTER. Put it in F2, SHFT+ENTER, and then drag it to fill F2:O15:
=IFERROR(INDEX($D$2:$D$15, SMALL(IF(($A2>=$B$2:$B$15)*($A2<=$C$2:$C$15), ROW($A$2:$A$15)-MIN(ROW($A$2:$A$15))+1), COLUMNS($F$2:F2))),"")
reformatted for easier explanation:
=IFERROR(
INDEX(
$D$2:$D$15,
SMALL(
IF(
($A2>=$B$2:$B$15)*($A2<=$C$2:$C$15),
ROW($A$2:$A$15) - MIN(ROW($A$2:$A$15))+1
),
COLUMNS($F$2:F2)
)
),
"")
From the inside out: ROW($A$2:$A$15) creates an array from 2 to 15, and MIN(ROW($A$2:$A$15))+1 scales it so that no matter which row the range starts in it will return the numbers starting from 1, so ROW($A$2:$A$15) - MIN(ROW($A$2:$A$15))+1 returns an array from 1 to 14.
We use this as the second argument in the IF clause, what to return if TRUE. For the first argument, the logical conditions, we take the same two conditions from the original formula: ($A2>=$B$2:$B$15)*($A2<=$C$2:$C$15). As before, this returns an array of true/false values. So the output of the entire IF clause is an array that consists of the row numbers where the conditions are true or FALSE where the conditions aren't met.
Take that array and pass it to SMALL. SMALL takes an array and returns the kth smallest value from the array. You'll use COLUMNS($F$2:F2) to determine k. COLUMNS returns the number of columns in the range, and since the first cell in the range reference is fixed and the second cell is dynamic, the range will expand when you drag the formula. What this will do is give you the 1st, 2nd, ... kth row numbers that contain matches, since FALSE values aren't returned by SMALL (as a matter of fact they generate an error, which is why the whole formula is wrapped in IFERROR).
Finally, we pass the range with the numbers we want to return (D2:D15 in this case) to INDEX along with the row number we got from SMALL, and INDEX will return the value from that row.
So FILTER is a lot simpler to look at, but you can get it done in an older version. This will also work in Google Sheets, and I added a second tab there with this formula, but array formulas work a little different there. Instead of using SHFT+ENTER to indicate an array formula, Sheets just wraps the formula in ARRAY_FORMULA(). Other than that, the two formulas are the same.
Since FALSE values aren't considered, it will skip those.

Trying to write an If statement that will show date of last item selected and change as updated

I have a series of data. That goes down the left side column of dates...
8/2, 8/3, and such. Then 2 columns over I have a column left for if the date is checks so 8/2:X, 8/3:X, 8/4: ,8/5: and such. I'm wanting to reference the date in the last field that has a X in it
A B C D
8/1 X
8/2 X
8/3 x
8/4
8/5
Trying to reference the date for the last A cell if there is an X in the D cell. So it would be for 8/3 would be the date. I'm not sure how to run with this.
I've looked through but now sure how to state an if statement
To return the date in column A that is in the same row as the last "X" (or "x") in column D:
=LOOKUP(2,1/(D:D="X"),A:A)
If you only want you cells to calculate if there's an X in column E you could write it like this:
=if(E3="x","", [put your calculation here] )
If you only care if there's anything there, not necessarily an "x" or if you're worried about case sensitivity:
=if(isblank(E3),"", [put your calculation here] )
Then just drag this formula down.
Based on what you had told me use the following formula in cell H1:
=maxifs($A$3:$A$24,$E$3:$E$24,"x")
If I understood correctly, you want to return the highest date with a X in it right? You can simulate a MAX IF with array formulas:
{=MAX(IF(E:E="X",A:A,FALSE))}
(You have to enter the formula with CTRL + SHIFT + ENTER to get those brackets and to it work properly)
The IF part inside that formula returns only a list of values (inside the TRUE part) that the condition returns true
Edit: If you're using Excel 2013 and above, you should check #Mark answer, as it is a cleaner way to do it
Just going to throw my hat in the ring here for a formula that will display the value of date that corresponds to the largest row number with an X in it in column D.
AGGREGATE will find the row number as follows:
=AGGREGATE(14,6,ROW(D:D)/(D:D="x"),1)
That can be nested inside an INDEX function to return the cell address of corresponding date which in turns display the value of the cell as follows:
=INDEX(A:A,AGGREGATE(14,6,ROW(D:D)/(D:D="x"),1))
While the above does work, AGGREGATE is performing array operations. Therefore full column references like D:D should be avoided and reduced to a smaller range that would closer match your data in order to avoid excess calculations on blank cells.
If you can ONLY enter the X consecutively without skipping a date, for instance you would not mark 8/6 X if 8/5 has no X marked, then you can find the last date using the following formula:
=INDEX(Column_Date,COUNTIF(Column_X,"X"))
The look up is case insensitive which means you can also enter lowercase x instead of uppercase X in column D or mix them up, the formula will return the same result anyway.
The solution is using COUNTIF function to locate the position of the last X in Column E of your worksheet, and then use INDEX function to return the corresponding date in Column A.
COUNTIF is a combination of COUNT and IF function. I guess this might be the "IF" function you are after.
Please note if the X is not marked consecutively day by day, you may need to use someone else' answer.
Cheers :)

How can you exclude a row from a SUM based on a cell's value?

I have a range that I want to sum, which is A2:M35. However, if column 'N' has the number 1 in it, I want to exclude that entire row from the sum. So, If N3 contains 1 I want to exclude the range A3:M3 from the sum calculation. Is this possible?
UPDATE:
I should also include that the 1 or 0 in column N is a flag to state whether this row should be excluded or not (1 = yes, 0 = no). However, this value is derived by checking whether any values in that row = "excluded". So, the additional complication here appears to be that even though the rows with "excluded" in them should be excluded, the sum calculation will show '#VALUE' as it believes some of the values are of the wrong data type (even though they shouldn't be included).
SIMPLE SOLUTION (with helper column)
If you can, to keep it simple, I'd just add a helper column.
So In cell O2:
=IF($N2=1,0,SUM($A2:$M2))
Drag that down to cell O35.
Then you can simply:
=SUM($O$2:$O$35)
COMPLEX SOLUTION (no helper column)
If you would like to avoid having to have a helper column cluttering up your sheet, you could use a SUMPRODUCT formula:
=SUMPRODUCT($A$2:$M$35,(LEN($A$2:$M$35)-LEN($A$2:$M$35))+NOT($N$2:$N$35))
HOW IT WORKS:
The first range (A2:M35) is the array (or in this case a range of excel cells) that you want to sum.
The SUMPRODUCT is going to take each value in that array and multiply it by each corresponding value in the next array we supply, then sum all the results together.
The problem is that the first array is a table, 13 values across and 34 values down. The second array (column N) is only 1 value across. SUMPRODUCT requires that all arrays are the same size.
To do this, we first create an array the correct size:
(LEN(A2:M35)-LEN(A2:M35))
LEN returns an array containing the number of characters in each cell supplied to it. If we take it away from it's self, we are left with an array of the correct size, filled with zeros.
We can then add the values in our smaller array (column N) to the zeros in the array of the correct size, this will fill all the columns with the correct value.
+NOT(N2:N35))
The NOT is there because we want to sum the rows which have a zero. All it is doing is swapping the zeros and ones in column N. So, all 1's become 0's and vice versa.
I hope you can follow my explanation. If not, please let me know and I will elaborate.

Display adjacent data to largest value within range while including duplicates

I'm attempting to grab each instance of the largest value within a range and display the five adjacent columns of data associated with each instance.
Sample
I was able to grab the largest data using this:
=IFERROR(LARGE($A$3:$A$100,ROW(1:1)),"NONE")
Unfortunately, I'm unable to figure out how to grab the adjacent data associated with the next instance of the duplicate value.
Note:
The archive table is in ascending order because that is the only way I can think of to ease copying and pasting the latest data.
If you do not want to use a sorted order, you can simply use this array formula at H3 and then auto-fill it to M8:
=IFERROR(INDEX($A$3:$F$18,SMALL(IF($A$3:$A$18=MAX($A$3:$A$18),ROW($1:$16)),ROW(A1)),COLUMN(A1)),"NONE")
This is an array formula and must be confirmed with Ctrl+Shift+Enter!
If you still have any questions, just ask ;)
EDIT:
The core is IF($A$3:$A$18=MAX($A$3:$A$18),ROW($1:$16)) which returns an array of rows and False like {FALSE,2,FALSE,FALSE,5,FALSE.....}. Knowing that SMALL ignores FALSE, You get with SMALL(...,1) the first row with the max value. (this way SMALL(...,2) will output the second row .....).
Using ROW(A1) and COLUMN(A1) as non static ref, you get the k for our SMALL and the column for our INDEX :)
Step 1)
Take your large function and compare it to the max from your source range, if it is less than max display none, if otherwise have it display the max.
Step 2)
Since your data is sorted in ascending order find the position in the range of the first instance of your maximum value.
=MATCH(MAX($A$3:$A$18),$A$3:$A$18,0)
We will use this to return the corresponding values in the adjacent columns by substituting it into an index formula for each column to be returned
=INDEX(B$3:B$18,MATCH(MAX($A$3:$A$18),$A$3:$A$18,0))
Place the above in I3 and copy it to right
Step 3)
Since your Data is sorted in order it means the next row down in column A of the max value will be still max value or end of the list. There for match value need to be increased by 1 for each consecutive row and needs some error handling
So we can use our formula from row 3 and adjust it as follows:
=IFERROR(INDEX(B$3:B$18,MATCH(MAX($A$3:$A$18),$A$3:$A$18,0)+1),"NONE")
OR
=IF(H4="NONE","NONE",INDEX(B$3:B$18,MATCH(MAX($A$3:$A$18),$A$3:$A$18,0)+1)
then change the +1 to a +2 then +3, etc however since we don't know wow many times this process will complete we can use the row reference which you started with and change the formula as follows:
=IFERROR(INDEX(B$3:B$18,MATCH(MAX($A$3:$A$18),$A$3:$A$18,0)+(row()-3)),"NONE")
OR
=IF(H4="NONE","NONE",INDEX(B$3:B$18,MATCH(MAX($A$3:$A$18),$A$3:$A$18,0)+(ROW()-ROW($A$3))))
the -3 being the hardcoded value for your first row of data which could also be changed to -ROW(A3) where A3 is a cell in the first row of your new table.
ERROR CORRECTION
I was adding the row bump to the match portion inside the match formula. This was a mistake. adding the +1,+2 or whatever needs to be added to the results of the match formula but still within the index formula. I have updated the formulas above to reflect this.

Resources