I'm trying to do what should be a simple enough task: find the index of the last cell in an array that matches a certain value.
I am using a MATCH-INDEX function combination that is giving me incorrect, inconsistent results. I can't figure out the problem.
In this example I'm using an array with values of 1 or -1 and trying to find the index of the last 1 in the array.
My understanding is (and using the Evaluate Formula tool confirms), INDEX(A1:F1=1,0) should return an array of
{FALSE,TRUE,FALSE,TRUE,FALSE,FALSE}
Why does the MATCH(TRUE,...) not give a result of 4 when it is fed this index array? MATCH works backwards in the array and should return the index of the last match, which is the TRUE value in the 4th position.
But here this code is giving a result of 6.
To make matters worse, if I use the same code but change around the array fed into INDEX, the results are inconsistent. When I'm changing array values to 1 or -1, sometimes the formula result changes and sometimes it doesn't, and I can't figure out why.
Below, changing only the 3rd array value changes the result of the formula from 4 to 6. WHAT IS GOING ON?!
It seems that match only finds first, but not last.
But I have a possible solution, I would do it the following way:
expect: your values are between H4 AND H27, the value you look for is "39"
1: find the rownum for all matching cells
I4 --> I27: IF(H4 = 39; ROW(H4); -1)
2: find the max rownum in I4 --> I27
MAX(....)
3: subtract the rownum of first cell (zero- based index)
MAX(....) - ROW(H4)
4: to get it in only one cell put into a Matrix function (CONTROL SHIFT ENTER makes CURLY BRACKETS)
{=MAX(IF($H$4:$H$27=39;ROW(H4:H27);-1))-ROW(H4)}
sounds strange, but works :)
Index doesn't return an Array, but a number, as the name tells you, the "INDEX" of the element, which fulfills your question. With the last Parameter you can decide, whether it has to be exact or could be the next smaller or next bigger value (last both options will only work with sorted Input)
You can solve your question either in more then one step as written in my last answer or with matrix functions, in your special case (now with columns instead of rows):
{=MAX(IF(A1:F1=1;COL(A1:F1);-1))}
Related
I am using Formula as:
=INDEX(B1:G1,H3)
Its not working. even tough similar posts are present I could not find the issue with this.
I am actually doing a max of all the Row entities like:
=MAX(B2:G2)
and doing
=INDEX(B1:G1,H2)
It works for first row and not for the other
INDEX wants the relative position not the value. You need to add a MATCH:
=INDEX($B$1:$G$1,MATCH(H2,B2:G2,0))
The Match will return the relative position of the match.
Index is using H3 to determine the column to return. That is why Row1 is functioning. It is finding column 6 (between B1:G1) and returning the result. It is only happenstance that it is the correct answer. If you were to substitute 5 into cell H2 it would likewise return Column5. In the other examples shown, the max function is returning a column index which is beyond what is in the array (B1:G1 is 6 columns long).
To correct, pair Index with the Match function. Match will search the array (B2:G2; B3:G3; etc.) and return the column number of the matching column, then pass that on to the Index function to return the proper result.
Your final formula will look like this:
=INDEX($B$1:$G$1, MATCH(H2, B2:G2, 0))
I know how to use index and match formulas to get the value or location of a matching cell. But what I don't know how to do is get that information when the cell I'm looking for isn't going to be the first match.
Take the image below for example. I want to get the location of the cell that says "Successful Deliveries". In this example there's a cell that matches that in rows 11 and 30. These locations can vary in the future so I need a formula that's smart enough to handle that.
How would I get the location of the second instance of "Successful Deliveries"? I figured I could use the "Combination 2 Stats" value from row 24 as a starting point.
I tried using this formula:
=MATCH("Successful Deliveries:",A24:A1000,0)
But it returns a row number of 7 which is just relative to the A24 cell I started my match at.
My end goal here is to get the value from the cell directly to the right of the second match of "Successful Deliveries".
In your formula, with no further intelligence, you can simply add 23 to adjust 7 to the result:
=MATCH("Successful Deliveries:",A24:A1000,0) + 23
You know that 23 is the number to add because you started your search on row 24.
The full answer is here:
https://exceljet.net/formula/get-nth-match-with-index-match
You use this formula:
=INDEX(B1:B100,SMALL(if(A1:A100 = "Successful Deliveries:",ROW(A1:A100) - ROW(INDEX(A1:A100,1,1))+1),2))
...where 2 is the instance you want.
Make sure to finish typing the formula by hitting ctrl-shift-enter. (You know you did this right because the formula gets curly brackets {})
HOW IT WORKS
Normally, we use INDEX / MATCH to find a value. The Index function gives you the nth value in a range, and the Match function determines which "n" is a match for our criteria.
Here we use INDEX the same way, but we need more intelligence to find that "n", since it's the second one that matches the criteria. That's where SMALL comes in. The Small function "gets the nth smallest value in an array". So we give Small the number of the desired instance (2 in this case) and we give it an array of blanks and the rows numbers of the rows we like.
We obtained the array of blanks and row numbers using the If function, asking it to check for our criterion (="Successful...") and making it return the row number where the criterion passes (=Row(A1:A100)). By using the If function as an array function (by giving it arrays and using ctrl-shift-enter) it can deliver a whole list of values.
Our final value is just one number because the Small function used the array from the IF to return just one thing: the second-smallest row we gave to it.
I'm trying to understand some legacy Excel file (it works, but I would really like to understand how/why it's working).
There is a sheet for data input (input sheet)and some code that is called to process data in the input sheet. I found out that number of rows in the input sheet is determined using a Lookup formula like this:
=LOOKUP(2;1/('Input sheet'!E1:E52863<>"");ROW(A:A))
"E" column contains names for import items and column is NOT sorted
"A" column does not contain anything special - I can replace it with B, C or whatever column and it does not affect the formula's outcome
According to what I have found about Lookup behaviour: •If the LOOKUP function can not find an exact match, it chooses the largest value in the lookup_range that is less than or equal to the value.
What does this ^-1 operation to the specified range? If E(x) is not empty -> it should turn into 1, but if it is empty - then it would be 1/0 -> that should produce #DIV/0! error...
1/('Input sheet'!E1:E52863<>"")
The outcome is the same, if I replace 2 with any positive number (ok, tried only some, but it looks like this is the case). If I change lookup value to 0, then I get #N/A error -> •If the value is smaller than all of the values in the lookup_range, then the LOOKUP function will return #N/A
I am stuck... can anyone shed some light?
LOOKUP has the rare ability to ignore errors. Conducting the 1/n operation will produce an error every time n is zero. False is the same as zero. So, for your formula, every empty cell produces an error in this calculation. All of those results are put in a vector array in the 2nd argument.
Searching for any positive value (the 1st argument) larger than 1 will result in LOOKUP finding the last non-error value in the above vector.
It also has the nice optional 3rd argument where you can specify the vector of results from which to return the lookup value. This is similar to the INDEX component of the the INDEX/MATCH combo.
In the case of your formula, the 3rd argument is an array that looks like this: {1;2;3;4;5;6;7;8;9;...n} where n is the last row number of the worksheet, which in modern versions of Excel is 1048576.
So LOOKUP returns the value from the vector in the 3rd argument that corresponds to the last non-error (non-blank cell) in the 2nd argument.
Note that this method of determining the last row will ignore cells that have formulas that result in a zero-length string. Such cells look blank but of course they are not. Depending on the situation, this may be precisely what you want. If, on the other hand you want to find the last row in column E that has a formula in it even if it results in a zero-length string, then this will do that:
=MATCH("";'Input sheet'!E:E;)
You might get some idea what the formula is doing (or any other formula) if you apply Evaluate Formula. Though since the principle is the same whether 3 rows or 52863 I'd suggest limiting the range, to speed things up if choosing Evaluate Formula. As usual with trying to explain formulae, it is best to start from the inside and work outwards. This:
'Input Sheet'!E1:E52863<>""
returns an array with a result for every entry in ColumnE from Row1 to Row52863. Since it is a comparison (<> does not equal) the result is Boolean - ie TRUE (not empty) or FALSE (is empty). So if only the first half of E1 to E52863 is populated, the result is {TRUE;TRUE;TRUE; ... and a LOT more TRUE; ... and FALSE ... and a LOT more ;FALSE and finally }.
Working outwards, the next step is to divide this array into 1. In arithmetic operations Boolean TRUE is treated as 1 and FALSE as 0, so the resultant array is {1;1;1; ... and a LOT more 1; ... and #DIV/0!... and a LOT more ;#DIV/0! and finally }.
This then becomes the lookup_vector within which LOOKUP seeks the lookup_value. The lookup_value you show is 2. But the array comprises either 1 or #DIV/0! - so 2 will never be found in it. As you have noticed, that 2 could just as well be 3, or 45 or 123 - anything as long as not a value present in the array.
That (not present) is necessary because LOOKUP stops searching when it finds a match. The fact that there is no match forces it to the end of the (valid) possibilities - ie the last 1. At this point, in my opinion, it would be logical to return "not found" but - I suspect merely a quirk, though very convenient - it returns that 1 - by its index number in the list, ie 52863 if all cells in E1:E52863 are populated.
Although the result_vector (Row(A:A)) is optional for LOOKUP it is required in this usage in effect to fix the start point for the index (effectively Row1, since an entire column). You might change that to say A3:A.. and the result would be the number of the highest populated row number in ColumnE plus 2 (3 -1).
I have the data table below, and I want that given a value 'x' look in 'A' and get the lower value in 'B'.
For instance 10.000 should return 0, 38.000 should return 7,8 and 900.000 should return 20. In my locale '.' means thousand separator and ',' is for decimals.
If possible I would like a formula which works in excel and gdocs. Thanks.
A B
0 0
37.500,01 7,8
45.000,01 9,1
58.345,62 11,4
120.206,02 13,6
208.075,91 15,7
295.242,83 17,2
382.409,77 18,2
600.000,01 20
I don't know about gdocs but in excel try the following.
=vlookup(value ; $A$1:$B$9 ; 2 ; 1)
where value is the value you are searching for.
The only prerequisite is that column A must be sorted in ascending order, as you have in your example.
You can use LOOKUP function, assuming lookup value in C2 use this formula in D2
=LOOKUP(C2,A$1:B$9)
The following would work for your needs:
=INDEX(B:B,MATCH(38,A:A,1),1)
Where 38 is the value we are looking up. Match will look in A:A and return the row where the value is less than 38 (because the third parameter of the match() formula is 1).
The Index will return the row in B:B that Match() just outputted.
If you search for 900,000 you're hoping to return the value from 600,000.01, because that's the last value it is higher than.
You can do this with an array formula, but I prefer to make Index do that work for me. As such, I present INDEX/MATCH/INDEX:
First, sort your data in A Largest to Smallest.
=INDEX($B$1:$B$9,MATCH(TRUE,INDEX(900000>$A$1:$A$9,0),0))
You can change 900,000 to whichever number you're looking for, or reference a cell with the value in it.
The second INDEX, the one nested in there, is examining each cell in Col A to determine if your target value is greater than them. It creates an array of TRUE's and FALSE's.
Next, you use MATCH to find the first TRUE in that array you created. That's the position of the first cell that your target is greater than (this is why we sort largest to smallest).
Once we know how far down the list it is, we use the first INDEX to look that far down Col B and grab the info you're looking for!
Play around with it, it makes sense once you think your way through it :) Good luck!
Here is the screenshot of my excel workbook
I do not understand why the value in cell j7 is 44 ?
j7 formula is =LOOKUP(1,(TRIM($D$2:$D$9)=TRIM(H7))/(TRIM($E$2:$E$9)=TRIM(I7)),$F$2:$F$9)
The result of the two arrays division is the following
{TRUE;TRUE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE}/
{TRUE;FALSE;TRUE;FALSE;FALSE;TRUE;FALSE;FALSE} =
{1;#DIV/0!;0;#DIV/0!;#DIV/0!;0;#DIV/0!;#DIV/0!}
Right ?
So I am looking for 1, basically the formula becomes
LOOKUP(1,{1;#DIV/0!;0;#DIV/0!;#DIV/0!;0;#DIV/0!;#DIV/0!},$F$2:$F$9)
Hence the result should be 10 but not 44 . . . . . ?
EDIT
When i correct my formula to =LOOKUP(1,1/(TRIM($D$2:$D$9)=TRIM(H7))/(TRIM($E$2:$E$9)=TRIM(I7)),$F$2:$F$9)
it works fine . Why ? Thank you everybody for giving the alternative solutions with match and index. I just can't understand why my first formula did not work. Any why when i add 1/ it MAGICALLY works ? ? ?
If the values are not in ascending order, and you are looking for a value within the range of values (as opposed to a value larger than anything in the range), LOOKUP may give unexpected results.
A different way to return the desired result is with a combination of INDEX and MATCH:
=INDEX($F$2:$F$9,MATCH(1,(TRIM($D$2:$D$9)=TRIM(H7))/(TRIM($E$2:$E$9)=TRIM(I7)),0))
entered as an array formula with ctrl-shift-enter
Note that with MATCH, looking for an exact match, the range does not need to be sorted.
Another formula that will return the correct results, and can be normally entered (assuming no duplicate entries in the first table):
=SUMPRODUCT((TRIM(H7)=TRIM($D$2:$D$9))*(TRIM(I7)=TRIM($E$2:$E$9))*$F$2:$F$9)
The explanation is:
with the first formula, the first array in the Lookup function contains zeros after the 1 value, in the third and sixth value of the array:
Lookup expects data to be sorted ascending and will return the first item less than or equal to the search value, starting from the last value in the array. In this case that is the zero value in the sixth position of the array.
The edited formula results in an array that contains only one number "1". All other values are Div errors. So the position of that "1" value is what Lookup will use.
Further explanation:
In your first formula you divide two arrays that contain TRUE or FALSE and the result contains 1, 0 and Div error values.
{TRUE;TRUE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE}/
{TRUE;FALSE;TRUE;FALSE;FALSE;TRUE;FALSE;FALSE} =
{1;#DIV/0!;0;#DIV/0!;#DIV/0!;0;#DIV/0!;#DIV/0!}
Including the 1/ in the formula will divide the first array of TRUE and FALSE values by 1, which returns an array that consists of either 1 or Div errors. Further dividing that array by the second array will only return 1 or Div errors, no zeros. The steps are
1/{TRUE;TRUE;FALSE;FALSE;FALSE;FALSE;FALSE;FALSE}/{TRUE;FALSE;TRUE;FALSE;FALSE;TRUE;FALSE;FALSE} results in
{1;1;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!}/{TRUE;FALSE;TRUE;FALSE;FALSE;TRUE;FALSE;FALSE} results in
{1;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!;#DIV/0!}
No zeros!
As mentioned, LOOKUP expects the values in the lookup_vector to be in ascending order. To gain the first match of columns H & I to columns D & E, I would suggest mathematically excluding the non-matching rows. What is left would be the matching rows. The following example supplies the first double match.
For J2
=INDEX($F$2:$F$9,MIN(INDEX(ROW($1:$8)+(($D$2:$D$9<>H2)+($E$2:$E$9<>I2))*1E+99,,)))
Fill down as necessary. This is a standard formula and can be easily modified to supply the 2nd, 3rd, etc matching values by swapping SMALL() in place of MIN(). Your results should be close to the following.