MATCH and LOOKUP returning unexpected values - excel

UPDATE WITH ANSWER:
I misunderstood the operation of MATCH and LOOKUP; they apparently do not automatically select the last value.
The updated formula is as follows:
=IF(
ISNUMBER( MATCH(2,INDEX(1/($D2=$D$1:$D1),0)) ),
($A2+$B2) - (LOOKUP(2,1/($D2=$D$1:$D1),$A$1:$A1)+LOOKUP(2,1/($D2=$D$1:$D1),$B$1:$B1))
,0 )
The main differences are that MATCH is now MATCH(2,INDEX(1/($D2=$D$1:$D1),0)) and LOOKUP is now LOOKUP(2,1/($D2=$D$1:$D1),$A$1:$A1).
Thanks barry houdini and Nanashi for their help!
I am working on what was supposed to be a simple spreadsheet, but one of my formulas is giving me unanticipated results. A screenshot of my data can be seen below:
![Spreadsheet Screenshot][1]
In column C, I am trying to get the time difference between the previous data point with the same type and the current data point. My formula (formatted for easier reading, and taken at cell C11) is as follows:
=IF(
NOT( ISNA( MATCH($D11,$D$1:$D10,1) ) ),
($A11+$B11)-(
LOOKUP($D11,$D$1:$D10,$A$1:$A10)+LOOKUP($D11,$D$1:$D10,$B$1:$B10)
),FALSE)
The cell numbering changes for the appropriate cell–for example, C10 references $D10 and the range $D$1:$D9, etc.
The conditional (NOT( ISNA( MATCH($D11,$D$1:$D10,1) ) ) checks to make sure that in column C there is a previous value that equals the current one; otherwise it returns FALSE, as it does in rows 1 and 12.
The calculation ($A11+$B11)-(LOOKUP($D11,$D$1:$D10,$A$1:$A10)+LOOKUP($D11,$D$1:$D10,$B$1:$B10) takes the current row date and time and subtracts the date and time in the row corresponding with the previous instance of the same type (the LOOKUP function is supposed to return the last occurrence of a value in an array, according to the documentation).
My issue is this: after row 12, the MATCH and LOOKUP functions are all picking up row 11 as the last row in which column C has the value of 1. I've tried testing them separately, and both functions return their appropriate values from row 11. For example, putting the following formula in E16: =MATCH(1,$D$1:$D16,1) returned 11 when I would expect the value to be 16.
What am I doing wrong?

The problem here is that MATCH is not programmed to return the index of the latest matching value. That said, a formula that gets this for us will solve the problem.
Enter the following formula in C2 and drag down:
=MOD((B2)-INDEX(B:B,SUMPRODUCT(MAX(($D$1:$D1=D2)*ROW($D$1:$D1)))),1)
Broken down, this is what each part does:
=SUMPRODUCT(MAX(($D$1:$D1=D2)*ROW($D$1:$D1))) 'Returns the row of the latest match.
=INDEX(B:B, SUMPRODUCT...) 'Returns the matching cell in Column B.
=MOD(B2 - INDEX(...),1) 'Gets the absolute difference, no need to add A and B.
Result:
Let us know if this helps.
EDIT: Changed formula as per Barry's correction.

Neither MATCH nor LOOKUP will find the "last occurrence" as you require with unsorted data. Try these versions for last row/match:
=MATCH(2,INDEX(1/($D11=$D$1:$D10),0))
=LOOKUP(2,1/($D11=$D$1:$D10),$B$1:$B10)
....for your specific requirement you can get both date and time in one LOOKUP, e.g.
=$A11+$B11-LOOKUP(2,1/($D11=$D$1:$D10),$A$1:$A10+$B$1:$B10)
which explicitly uses the dates in column A - if you have data for every day, so you can always assume that change of day is to the next day then you can leave out the date references and use MOD like this:
=MOD($B11-LOOKUP(2,1/($D11=$D$1:$D10),$B$1:$B10),1)

Related

Can Vlookup return a formula in excel?

I am trying to use the VLOOKUP function in excel to return a formula. I've found plenty here on SO about using VLOOKUP but have a slightly different question.
I am trying to get the week's average temperature (from column B) before a specified day (in column C) that can be found in column A.
Put more simply:
If date in cell C2 exists in column A, return average from range in column B.
So far this is the closest I've got (thanks to other QAs on SO):
=VLOOKUP(C2, A1:B660, AVERAGE(B124:B130), FALSE)
However using the AVERAGE function in place of column number (which would usually be the argument used) does not seem to work.
I've attached a very simplified picture of my data:
If VLOOKUP is the wrong function to handle this what would be a better approach?
I'm running Excel 15.32 on a Mac.
EDIT:
Dummy data:
https://www.dropbox.com/s/rblk943k2zf71tw/dummy%20data.xlsx?dl=0
What about:
=AVERAGE(OFFSET($B:$B,MATCH($C2,$A:$A,0)-1,0,-7))
This assumes a header in row 1.
Find the matched date in column A from the entry in C2
Create an array of column B values, counting back a week from that point
Average that array.
You can fill this down column D, for example, and it will adjust as necessary.
One problem with the OFFSET function is that it is volatile, so all the formulas would recalculate if anything is changed on the worksheet.
The following, at least in later versions of Excel, should be non-volatile:
=AVERAGE((INDEX($A:$B,MATCH($C2,$A:$A)-6,2,1):INDEX($A:$B,MATCH($C2,$A:$A,0),2)))
I will leave it to you to handle the errors from checking dates in rows 2-6.
Use INDEX as a cell reference with MATCH locating the primary date and using that position as well as that position minus 6 for the range.
=AVERAGE(INDEX(B:B, MATCH(C2, A:A, 0)-6):INDEX(B:B, MATCH(C2, A:A, 0)))
I think what you are looking for is this:
=IF(COUNTIF(A1:A660,C2)=0,0,AVERAGE(B124:B130))
This says if C2 cannot be found in A1:B660, then return 0, otherwise return the AVERAGE(B124:B130).
I would recommend a different approach. Still using VLOOKUP, I would go with the below. However there will be an easier/more elegant way to get this done.
Use VLOOKUP to return a value if found. Really, you don't care about the value it returns.
=VLOOKUP(C2,A:B,2,FALSE)
Wrap in an IFNA to handle dates not found. Here I've got it to return -1.
=IFNA(VLOOKUP(C2,A:B,2,FALSE),-1)
Use an IF statement to work out an average if VLOOKUP returns something, and do something else if it's not found.
=IF(IFNA(VLOOKUP(C2,A:B,2,FALSE),-1)<>-1,AVERAGE(B124:B130),"do something else")

Given a value in an table of values in Excel, how to return the corresponding value from the first column/row

I have the following data-set:
Given a number that occurs only once in the data range B2:E5, I would like to use a formula that returns the corresponding date value. The date values are given in the first column (A2:A5). I would also like to be able to return the corresponding hour value using a similar formula. Note, it is not necessary to return the hour and the date for a given value using the same formula.
Example: In the image of the data-set above – given the number 5, I would like to return the corresponding date. In this case, the formula would return the date value 03/01/2013. Similarly, I would also like to be able to return the "hour 3", given the same initial value.
Maybe the answer to this question is quite straight forward, but as of yet I have had no luck in figuring it out. Some things that I have tried, but to no avail, are the following: VLOOKUP/HLOOKUP, LOOKUP, a combination of INDEX+MATCH, and a combination of INDEX+MATCH+MATCH.
Any help is very much appreciated.
Given that Number to Find is unique, you can use this:
For the date in Column 1
=INDEX(Table1[Column1],MAX((NumToFind=Table1 )*ROW(Table1 ))-1)
For the hour in the Headers fields:
=INDEX(Table1[#Headers],1,MAX((NumToFind=Table1)*COLUMN(Table1)))
The above formulas are array formulas and must be confirmed by holding down CTRL + SHIFT ENTER
The -1 in the first formula is to compensate for the fact that the table starts in Row 1, with the data starting in Row 2, and the formula determines the absolute row number; if it starts other than in A1, different compensating values will be required.

Excel formula to lookup the last value in a column and return the value of the adjacent cell

I have the following formula to return the value of the last value in a column:
=LOOKUP(2,1/(D:D<>""),D:D)
What I need now is to return the value of the cell adjacent to it as well. (It will not necessarily be the last value in that column and the info in Column D could have duplicates.
If your data looks like this:
A 1
A 2
A 3
B 4
B 5
B 6
C 7
To get last value this will do the trick:
=INDIRECT("B"&COUNTA(A:A))
And to get last where value is A:
=INDIRECT("B"&MATCH("A",A1:A7,0)+COUNTIF(A1:A7,"A")-1)
Just use next column:
=LOOKUP(2,1/(D:D<>""),E:E)
Ok, So I have found an answer by playing around with array formulas.
The problem was that this is a stock control sheet where there are changes made at multiple times, each recorded in the next available row. There is always a date (Column E) but not necessarily a Supplier, as it might be stock moving out. When a Supplier delivers, the Supplier name is recorded in Column D. In D1 the last supplier is then shown with the following formula.
=LOOKUP(2,1/(D:D<>""),D:D)
I want to then see what date it was last received. The formula I found that works is as follows (Array Formula):
=INDEX(E:E,MAX(IF(D:D=D1,ROW(D:D)-ROW(INDEX(D:D,1,1))+1)))
This is generally how I do it:
=XMATCH(FALSE,ISBLANK(A:A),0,-1)
This is what each part does:
Parameter
Explanation
FALSE
Instructs Excel to find the first instance of FALSE that it finds
ISBLANK(A:A)
Takes in the column A:A and notionally assigns a value to every item in the column
0
Means we want an exact match. Probably not necessary to put in, but I think it's good practice anyway
-1
Instructs Excel to start the search at the bottom/right of the range and work up/left. If you change this to 1 (the default), Excel will begin the search at the top/left and work down/right
So, taken together, this will search from the bottom of the column A:A, until Excel finds the first cell that is not blank, and return that cell.
Also, yes, this equation can be changed to a row format (e.g. 1:1), and can take a smaller range (e.g. A1:A20), but it cannot take a 2-dimensional range (e.g. A1:B20).
As a practical matter, this approach is much faster than other approaches (and much faster than you'd think, given it's evaluating against every row/column in the range), and won't get fooled by columns that have empty spaces in them (like with a COUNTA style approach).

EXCEL - SUM column if date matches TODAY()

I've tried using SUMIF to obtain my results but it doesn't work properly.
I have a row of dates (XX/XX/XXXX format) and I would like to check this row for the current date.
If the row contains the current date, then I would like to sum the total of that column and row 5-20.
For example - today is 10/13/2016. I would like to search for TODAY() in a certain row (Row 1 for example), and if TODAY() is found, then total this column from row 1 down to row 3.
--A-- --B--
10/13/2016 10/14/16
1 50 10
2 10 4
3 5 6
The result should be 65 only IF the date matches TODAY().
I've also checked on giving the column letter based on the date but with no luck.
Any tips are appeciated! Thank you.
I think you can achieve this with a simple IF and a SUM
i.e. in your example above if you want the result to appear on the bottom row you can just use:
=if(B2=today(), sum(B3:B5), "")
This will display the sum at the bottom of the column for columns where the date = today and a blank in the other columns
You need to use the OFFSET function. You can find the documentation here: https://support.office.com/en-us/article/OFFSET-function-C8DE19AE-DD79-4B9B-A14E-B4D906D11B66
In your particular example the following formula will work:
=SUM(OFFSET(D2, 1, MATCH(B2, $D$2:$F$2, FALSE) - 1, 20))
You can see the formula working below. Assuming you know what the SUM formula is doing, I will explain what the OFFSET formula is doing:
First Parameter: Says start at cell D2
Second Parameter: This is how many row up/down do you want to do. In your case you need start at row below the date so '1' it is.
Third Parameter: This is how many columns to the right do you want to
go. Well the number of columns you want to go is based upon where
your date is. So this uses the match formula to figure out how far
to the right to go.
Fourth Parameter: This is how many row do you want to include. I just picked 20 to include the 20 rows below the selected cell.
You obviously need to modify the parameters a little bit to fit your exact data shape.
So I'll give it a shot:
{=SUM(HLOOKUP(TODAY(),Table_With_Dates_and_Three_Rows_Cell_Reference,{2,3,4}))}
NB: Don't type {} but put the formula inside it and then hit Ctrl+Shift+Enter to create what is called an array formula (it does array calculations element by element then submits the aggregating function value---in this case that is sum).

Three Dimensional Lookup Using INDEX/MATCH

This was taken and improved slightly from Question that has since been deleted
For those who can see deleted posts, it was taken from here: https://stackoverflow.com/questions/39793322/three-dimensional-lookup-no-concatenate-or-named-ranges-excel
I'm trying to do a three dimensional lookup without named ranges or concatenates. Simplified, my data is on the form:
Column1 Column2 Column3
Scott
P 1 2 3
M 4 5 6
N 7 8 9
George
P 10 11 12
M 13 14 15
N 16 17 18
I now want to search for a specific Name and then for a specific letter within that names table, I then want to match this row number with a specific column.
I tried a simple INDEX/MATCH:
=INDEX(A:D,MATCH("M",A:A,0),MATCH("Column1",1:1,0))
And that works for the fist name but not any others as it finds the first instance of M.
How do I modify it to look for a different name?
I have answered below, but want to see if someone has a better solution.
I used an IF() statement array formula to find what the P row number was after the George row... I also needed to use the MIN() function to get the first P row number after the name.
Beyond that, it's a simple INDEX() function.... that racked my brain for over an hour :).
=INDEX($A$1:$D$9,MIN(IF((ROW(A1:A9)>MATCH($F$4,A1:A9,0))*(A1:A9=$F$5),ROW(A1:A9),"")),MATCH($F$6,$A$1:$D$1,0))
Don't Forget!
Use Ctrl+Shift+Enter when finishing the formula, so it gets evaluated as an array formula.
You can use two other INDEX/MATCH's inside the first MATCH to set the lookup range. Then you simply need to add the MATCH() to find the absolute position of the name.
=INDEX(A:D,MATCH($H$4,INDEX(A:A,MATCH($H$3,A:A,0)):INDEX(A:A,MATCH($H$3,A:A,0)+4),0)+MATCH($H$3,A:A,0)-1,MATCH($H$5,$1:$1,0))
This one works better and does not have a size constraint:
=INDEX(A:D,MATCH(F4,INDEX(A:A,MATCH(F3,A:A,0)):A1040000,0)+MATCH(F3,A:A,0)-1,MATCH(F5,A1:D1,0))
You can do this just by adding the results of two matches together. One match for the names plus one match for the letter equals the total row.
=INDEX(A:D,MATCH(G5,A3:A5,0)+MATCH(G3,A:A,0),MATCH(G4,1:1,0))
In other words: Index(All of the Data, Match(Name, In name column, exact) + Match(Letter, In letter column, exact), Match(Column name, in Column row, exact)
Screen capture of working sheet
My answer attempts the general case with only one caveat:
That a letter is single character text, and a name is more than 1 character. Otherwise i feel there is no difference logically between letters and names, and it is then impossible to really do...
RE-EDIT for better function construction:
{=INDEX($A$1:$D$17, MATCH($H$3,$A1:$A17, 0)+MATCH($H$4, INDEX($A1:$A17, MATCH($H$3,$A1:$A17, 0)):INDEX($A:$A, SMALL(IFERROR(MATCH($H$3,$A1:$A17, 0)+POWER(SQRT(IF(LEN($A$1:$A$17)>1, ROW($A$1:$A$17), 0)-MATCH($H$3,$A$1:$A$17, 0)), 2)-1, ROWS($A$1:$A$17)), 2)), 0)-1, MATCH($H$5, $A$1:$D$1, 0))}
This uses an array formula along column A, and checks if the length is > 1 and throws the row nums into an array, with letters given a 0.
Then match row of unique name(e.g. George) is subtracted from each.
We then use a min(of all other name rows, with the last data row as the final default - SMALL function with 2 parameter) to find the next name row(or last data row if there is no following name).
Rest is standard index/match etc.
It will correctly return #N/A if there is no such letter under the chosen name...
My dataset is A1:A17, and the formula could use A:A instead each time, but the array calc inside the IF needs the A1:A17 for speed.
EDIT for better function construction:
If we wanted to avoid editing the formula when the data length changes, then we could let full column references of A:A go through the entire construction(and lose speed/efficiency) with the last data row in colA calculated via ROWS(A:A):
Re-edit:
{=INDEX($A:$D, MATCH($H$3,$A:$A, 0)+MATCH($H$4, INDEX($A:$A, MATCH($H$3,$A:$A, 0)):INDEX($A:$A, SMALL(IFERROR(MATCH($H$3,$A:$A, 0)+POWER(SQRT(IF(LEN($A:$A)>1, ROW($A:$A), 0)-MATCH($H$3,$A:$A, 0)), 2)-1, ROWS($A:$A)), 2)), 0)-1, MATCH($H$5,1:1, 0))}
It really depends on the setup...
Edit again for version which takes blanks as separators for names
If you want to use blanks as the separator for names, where no blanks are in the data results, but blanks appear in columns B to D where there is a name, then a tiny change in the above formulae will result in this:
=INDEX($A$1:$D$17, MATCH($H$3,$A$1:$A$17, 0)+MATCH($H$4, INDEX($A:$A, MATCH($H$3,$A:$A, 0)):INDEX($A:$A, SMALL(IFERROR(MATCH($H$3,$A:$A, 0)+POWER(SQRT(IF($B$1:$B$17="", ROW($A$1:$A$17), 0)-MATCH($H$3,$A$1:$A$17, 0)), 2)-1, ROWS($A$1:$A$17)), 2)), 0)-1, MATCH($H$5, $A$1:$D$1, 0))
This means that the names and letters do not have to be any specified length, but just one proviso is that blanks appear in the row with the name.
A small amendment to the condition to find the end range to search for the letter by replacing this: SQRT(IF(LEN($A$1:$A$17)>1, with this:
SQRT(IF($B$1:$B$17="",
I would use the area (4th parameter) of Index(). Below is a screenshot of test data. This example assumes the same columns and keys are sorted and consistent.
This works by using (Range1,Range2) as the first parameter of index. For the 4th parameter of index, use N for which area in the () you want Index to return.
I think this may be slightly tidier, and a little easier to modify maybe.
=INDEX(OFFSET(INDIRECT("A"&MATCH($H$3,$A:$A,0),TRUE),0,0,4,4),MATCH($H$4,$A:$A,0),MATCH(H5,$1:$1,0))
Using offset to create the range first, we're able to use the name from H3 to set that up, and then beyond that we are just indexing within that new range.
Now this is still dependendent on staying in Column A for the names.
Assuming the format of the data is always Name then P, M and N this formula does the work:
=INDEX($A:$D,
MATCH($H$3,$A:$A,0)
+LOOKUP($H$4,{"P",1;"M",2;"N",3}),
MATCH($H$5,$1:$1,0))
This solution works on almost all conditions. One restriction I found is when one of the subjects (Names) does no have data for any of the details (letters), but as of now the same occurs with all the other answers.
The formula assumes the data is located at B6:F30 (in order to ensure it can be applied regardless of the source range location).
The formula uses the Index\Match functions:
First, a MATCH to retrieve the position of the Name:
MATCH($H8,$B$6:$B$30,0)
With that info it uses INDEX to build a range that is used to obtain the position of the Detail (letter) using a second MATCH Function:
+ MATCH($I8,INDEX($B$6:$B$30, 1 + MATCH($H8,$B$6:$B$30,0))
:INDEX($B$6:$B$30,ROWS($B$6:$B$30)),0),
Adding the results of the first and second MATCH functions obtains the position of the Name`Detail` combination and uses it in an Index to the entire data. The position of the Data Column required is obtained with a Match:
INDEX($B$6:$F$30, 1st.MATCH + 2nd.MATCH,
MATCH(J$6,$B$6:$F$6,0))
With the results located at G6:L30 enter this formula in J8 then copy to J8:L30:
= INDEX( $B$6:$F$30,
MATCH( $H8, $B$6:$B$30, 0)
+MATCH( $I8, INDEX( $B$6:$B$30 , 1 + MATCH( $H8, $B$6:$B$30 ,0))
: INDEX( $B$6:$B$30, ROWS($B$6:$B$30) ),0),
MATCH( J$6, $B$6:$F$6, 0)),"")
This solution works in all conditions discussed so far (let me know of any condition that it does not work and I’ll try to cover it).
I’m posting this as a separated answer as the formulas applied in prior answer rightly apply to the conditions stated in them, as such they will be useful to users with those specific scenarios, so they don’t need to apply these long formulas.
This formula assumes the data is located at B6:E30 (in order to ensure it can be applied regardless of the source range location).
This formula uses the Index\Match functions and it’s a Formula Array.
FormulaArrays are entered pressing [Ctrl] + [Shift] + [Enter] simultaneously, you shall see { and } around the formula if entered correctly
Syntax:
=IFERROR(INDEX(DataRng,
MATCH(Value1,NamesRng,0)
+IFERROR(MATCH(Value2,INDEX(NamesRng,
1+MATCH(Value1,NamesRng,0))
:INDEX(NamesRng, IFERROR(MATCH(Value1,NamesRng,0)
+MATCH("#",IF((INDEX(Col1Rng,1+MATCH(Value1,NamesRng,0))
:INDEX(Col1Rng,ROWS(NamesRng)))="","#","!"),0),
ROWS(NamesRng))),0),NA()),MATCH(ValCol,DataHdr,0)),"")
Arguments:
Assuming the data is located at B6:E30.
Value1= Name to be found in Data, i.e. George, Scott, etc.
Value2= Detail to be found in Data, i.e. Detail1, Detalle2, etc.
ValCol = Column to be found in Data i.e. Column1, Column2, etc.
DataRng= $B$6:$E$30
DataHdr= $B$6:$E$6
NamesRng= $B$6:$B$30
Col1Rng= $C$6:$C$30
1st MATCH: Retrieves the position of the Name:
MATCH(Value1,NamesRng,0)
2nd MATCH: Retrieves the end position of the Name’s corresponding Details, which is determined by a blank value in column C or the end of the data range:
MATCH("#",IF((INDEX(Col1Rng, 1 + 1stMATCH)
:INDEX(Col1Rng,ROWS(NamesRng)))="","#","!"),0),
Builds a Range (vRange): With the Names's Details using the 1st and 2nd match functions. If 2nd Match returns an error then it uses the last row of the Data range:
INDEX(NamesRng, 1 + 1stMATCH )
:INDEX(NamesRng, IFERROR( 1stMATCH + 2ndMATCH, ROWS(NamesRng)))
3rd MATCH: Retrieves the position of the Detail within the vRange. It returns #NA if the combination is not present.
IFERROR(MATCH(Value2, vRange,0), NA())
Adding the results of the 1st and 3rd match functions obtains the Row index of the Name`Detailcombination or#NAif no found.
The Column index is obtained with a Match from the Header of the Data.
It then applying the INDEX function to the Data Range returns the value of theName\Detail\Columncombination.
If theName\Detail` combination is not found it returns blank.
=IFERROR( INDEX( DataRng, 1stMATCH + 3rdMATCH, MATCH(Column,DataHdr,0)),"")
With the results located at H6:L37 enter this Formula Array in J8 then copy to K8:L37 and to J9:L37:
=IFERROR( INDEX($B$6:$E$30,
MATCH($H8,$B$6:$B$30,0)
+IFERROR( MATCH($I8, INDEX($B$6:$B$30,
1+MATCH($H8,$B$6:$B$30,0))
:INDEX($B$6:$B$30, IFERROR(MATCH($H8,$B$6:$B$30,0)
+MATCH("#", IF((INDEX($C$6:$C$30,1+MATCH($H8,$B$6:$B$30,0))
:INDEX($C$6:$C$30,ROWS($B$6:$B$30)))="","#","!"),0),
ROWS($B$6:$B$30))),0),NA()),
MATCH(J$6,$B$6:$E$6,0)), "")
Wow... So many solutions already.
I think a simpler solution could be using offset to get a more generic answer.
=INDEX($A$1:$D$9, MATCH($G$3,OFFSET($A$1,MATCH($G$2,$A$1:$A$9,0),0,3,1),0)+MATCH($G$2,$A$1:$A$9,0), MATCH($G$4,$B$1:$D$1,0)+1)
The only variable to look for is 3 which is the number of M/N/P options present because that will affect the number of rows. Otherwise, the solution works fine in all possible scenarios and different orders.
When I have more than two inpunts for a data search I prefer to have the data organized as shown in the figure, so that I can use a pivot table and get it to organize the data in rows and columns as I like.
Then I use GETPIVOTDATA to search for a value.
Cell G9 contains this formula:
=GETPIVOTDATA("Value";$F$3;"Name";G15;"Letter";G16;"Column";G17)

Resources