132

Does anyone know the formula to find the value of the last non-empty cell in a column, in Microsoft Excel?

Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
Michael S
  • 4,352
  • 5
  • 23
  • 29
  • I'd be looking to VBA for this. – David Heffernan Mar 27 '11 at 21:36
  • If you don't want to have to worry about sort order, don't want to enter an array formula, and don't have gigantic negative numeric values or text values starting with "ZZZZZZZZZZZZZZZZZZZZZZZZ" in the column you're trying to find the last non-empty cell in, then a far more efficient formula is a combination of Max(), Iferror(), and Match() like this: =MAX(IFERROR(MATCH(-9.99999999999999E+27,Sheet2!A:A,-1),0),IFERROR(MATCH("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",Sheet2!A:A,1),0)), which returns the row and can then be indirectly referenced to return the value as you see fit. – Josh Fierro Oct 16 '21 at 19:25

26 Answers26

146

Using following simple formula is much faster

=LOOKUP(2,1/(A:A<>""),A:A)

For Excel 2003:

=LOOKUP(2,1/(A1:A65535<>""),A1:A65535)

It gives you following advantages:

  • it's not array formula
  • it's not volatile formula

Explanation:

  • (A:A<>"") returns array {TRUE,TRUE,..,FALSE,..}
  • 1/(A:A<>"") modifies this array to {1,1,..,#DIV/0!,..}.
  • Since LOOKUP expects sorted array in ascending order, and taking into account that if the LOOKUP function can not find an exact match, it chooses the largest value in the lookup_range (in our case {1,1,..,#DIV/0!,..}) that is less than or equal to the value (in our case 2), formula finds last 1 in array and returns corresponding value from result_range (third parameter - A:A).

Also little note - above formula doesn't take into account cells with errors (you can see it only if last non empty cell has error). If you want to take them into account, use:

=LOOKUP(2,1/(NOT(ISBLANK(A:A))),A:A)

image below shows the difference:

enter image description here

Dmitry Pavliv
  • 35,333
  • 13
  • 79
  • 80
  • 1
    thanks for explanation and nice picture. Still some questions remain open: 1. The documentation to `LOOKUP` says that the last argument `result_vector` is optional. However if I omit it, I get a very strange result which I do not understand. – Honza Zidek Jun 04 '14 at 08:25
  • 2. The documentation says "If the `LOOKUP` function can't find the `lookup_value`, the function matches the largest value in `lookup_vector` that is less than or equal to `lookup_value.`" If I use `=LOOKUP(2,A:A<>"",A:A)` without generatin the `#DIV/0!` error by `1/...`, it seems that it returns some value in the middle of the vector. I have not found what is the exact functionality in this case. – Honza Zidek Jun 04 '14 at 08:29
  • 2
    1) optional does not mean unnecessary, if you ommit it, lookup returns value from `1/(A:A<>"")` (always `1` exept empty col A). 2) `A:A<>""` returns boolean array, but you need number array - `=LOOKUP(2,1*(A:A<>""),A:A)`, but it also not working as you want - lookup always relies on the fact that the array is sorted. Since the last element is always less than the lookup value, it makes no sense to view the rest of the array and lookup simply returns it (or corresponding value from _result_vector_). So, `=LOOKUP(2,1*(A1:A10<>""),A1:A10)` always returns value from `A10` (if it's not error). – Dmitry Pavliv Jun 04 '14 at 16:36
  • Thanks again! I have not realized that 1. `LOOKUP()` without the 3rd parameter returns the value from the 2nd parameter and **not** from the underlying vector, 2. Excel searches *backwards* starting from the last field of the vector. Will you your last comment also to your post? You would increase its value even more :) I will then delete all my comments under it so we keep the thread clean. – Honza Zidek Jun 06 '14 at 23:36
  • 1
    It's just my guess, most likely it's correct, however nobody knows (exept developers) which search algorithm is used in lookup. Maybe it is just backward loop or [binary search](http://en.wikipedia.org/wiki/Binary_search_algorithm), or more sophisticated algorithm.. we don't know..So, I think it's not worth to add in response unproven info, but just guess.. – Dmitry Pavliv Jun 07 '14 at 06:08
  • 1
    But your explanation is useful - I do not understand it as a description of implementation, but rather as a logic of the function behaviour. – Honza Zidek Jun 07 '14 at 08:57
  • 1
    Very nice explanation. Having recently shaved minutes off calculation by moving formulae from array formulae to non-array versions, I'm always up for logic that works without requiring array formulae. – tobriand Jul 29 '14 at 15:43
  • It gets the value, is there anyway to modify it so it gets the index/row/column number ? – Enissay Sep 27 '16 at 11:43
  • 1
    @Enissay, yes, this one will return row number `=LOOKUP(2,1/(A:A<>""),ROW(A:A))` but adding `ROW` function will add "volatility" effect - formula will be recalculated each time any cell on the wroksheet changed – Dmitry Pavliv Sep 27 '16 at 12:53
  • 1
    Hey there. Thanks for posting this. Just curious, I found the exact same formulas and nearly the same wording over at https://exceljet.net/formula/get-value-of-last-non-empty-cell . Is that also you or someone else? There is no obvious connection. – Solomon Rutzky Oct 03 '16 at 22:21
  • I was pleased to find that this allowed me to put the formula in the very same column it referenced, without a circular reference error. I'm not really sure why that worked, since something like e.g. =A:A does raise a circular reference error if you put it in the same column it references. – Greg Lovern May 11 '17 at 22:59
  • This gives me the **first** row, of the column, not the last. – Adambean Jul 25 '19 at 08:05
  • BTW, if you use older version of LibreOffice (I know this isn't that common so I won't add it to the original answer): `=LOOKUP(2,1/(A:A<>""),ROW(A:A))` would just give you #DIV/0! because in those version LOOKUP doesn't do what it does as Excel, which ignored the division error. Then you can change it to `=LOOKUP(2,1+(A:A<>""),ROW(A:A))` because when it equals empty, it converts that express to 1+0 which does not equal 2, but it is isn't empty it converts to 1+1 which does equal 2. The "bug" was fixed in 6.2 to match Excel, https://bugs.documentfoundation.org/show_bug.cgi?id=117016 – 1283822 May 07 '21 at 19:42
92

This works with both text and numbers and doesn't care if there are blank cells, i.e., it will return the last non-blank cell.

It needs to be array-entered, meaning that you press Ctrl-Shift-Enter after you type or paste it in. The below is for column A:

=INDEX(A:A,MAX((A:A<>"")*(ROW(A:A))))
Marc.2377
  • 7,807
  • 7
  • 51
  • 95
Doug Glancy
  • 27,214
  • 6
  • 67
  • 115
  • @ Jean-François, Sorry, I acknowledged that in a comment last night but put it in the original post by mistake. It does work in XL 2007 and 2010. Thanks. – Doug Glancy Mar 27 '11 at 16:41
  • Doesn't work for me in Excel 2007. I pasted the exact formula. I have a column (A), where the values are =ROW() all the way down to 127ish, and the formula returns "1" – DontFretBrett Oct 04 '13 at 19:09
  • 2
    @DontFretBrett, Be sure to enter it as an array formula with Ctrl-Shift-Enter as specified in the answer. – Doug Glancy Oct 04 '13 at 20:14
  • @DougGlancy fail on my part. Thank you! – DontFretBrett Oct 07 '13 at 16:57
  • 2
    The important difference is that this one returns a cell reference (so it can be passed to e.g. `CELL()` or `OFFSET()`), whereas [the more upvoted one](http://stackoverflow.com/a/22192270/11683) returns a value without telling you where it is. – GSerg Jan 01 '17 at 15:31
  • @DougGlancy Can you please provide an explanation on how this works? – Slavo Jun 27 '17 at 21:18
  • 2
    `=INDEX($C:$C,SUMPRODUCT(MAX(($C:$C<>"")*(ROW($C:$C)))))` ... with SUMPRODUCT hence does not need to be entered as array formula – Rosetta Nov 03 '17 at 08:03
  • used this modded to return only last (previous?) non-empty cell repeated until the next non-empty cell to repeat. `{=INDEX($A$1:$A1,MAX(($A$1:$A1<>"")*ROW($A$1:$A1)))}` – cliffclof Mar 13 '21 at 06:24
25

Here is another option: =OFFSET($A$1;COUNTA(A:A)-1;0)

iDevlop
  • 24,841
  • 11
  • 90
  • 149
22

I know this question is old, but I'm not satisfied with the answers provided.

  • LOOKUP, VLOOKUP and HLOOKUP has performance issues and should really never be used.

  • Array functions has a lot of overhead and can also have performance issues, so it should only be used as a last resort.

  • COUNT and COUNTA run into problems if the data is not contiguously non-blank, i.e. you have blank spaces and then data again in the range in question

  • INDIRECT is volatile so it should only be used as a last resort

  • OFFSET is volatile so it should only be used as a last resort

  • any references to the last row or column possible (the 65536th row in Excel 2003, for instance) is not robust and results in extra overhead

This is what I use

  • when the data type is mixed: =max(MATCH(1E+306,[RANGE],1),MATCH("*",[RANGE],-1))

  • when it's known that the data contains only numbers: =MATCH(1E+306,[RANGE],1)

  • when it's known that the data contains only text: =MATCH("*",[RANGE],-1)

MATCH has the lowest overhead and is non-volatile, so if you're working with lots of data this is the best to use.

W5ALIVE
  • 381
  • 3
  • 9
  • 1
    This is a good answer (+1), but there are still some loopholes that this method can miss. For example if there are cells with logical values (`TRUE`/`FALSE`), they will go undetected. Despite the fact that this answer isn't bulletproof, I still think this answer has the least impact on performance. – ImaginaryHuman072889 Sep 06 '18 at 12:54
  • 3
    Match returns an index, not the value of the cell. You can use the index function to get the value. Also, you should probably include something about what [RANGE] is supposed to be. I selected the entire column by using "N:N". Here is an example that worked for me: `=INDEX(N:N,MATCH(1E+306,N:N,1))` – swimfar Sep 07 '18 at 18:13
  • Yes I've run timings on this and it appears to be the fastest by some margin – Tom Sharpe Apr 27 '23 at 16:45
19

Inspired by the great lead given by Doug Glancy's answer, I came up with a way to do the same thing without the need of an array-formula. Do not ask me why, but I am keen to avoid the use of array formulae if at all possible (not for any particular reason, it's just my style).

Here it is:

=SUMPRODUCT(MAX(($A:$A<>"")*(ROW(A:A))))

For finding the last non-empty row using Column A as the reference column

=SUMPRODUCT(MAX(($1:$1<>"")*(COLUMN(1:1))))

For finding the last non-empty column using row 1 as the reference row

This can be further utilized in conjunction with the index function to efficiently define dynamic named ranges, but this is something for another post as this is not related to the immediate question addressed herein.

I've tested the above methods with Excel 2010, both "natively" and in "Compatibility Mode" (for older versions of Excel) and they work. Again, with these you do not need to do any of the Ctrl+Shift+Enter. By leveraging the way sumproduct works in Excel we can get our arms around the need to carry array-operations but we do it without an array-formula. I hope someone out there may appreciate the beauty, simplicity and elegance of these proposed sumproduct solutions as much as I do. I do not attest to the memory-efficiency of the above solutions though. Just that they are simple, look beautiful, help the intended purpose and are flexible enough to extend their use to other purposes :)

Hope this helps!

All the best!

zero323
  • 322,348
  • 103
  • 959
  • 935
Santiago
  • 191
  • 1
  • 3
10

This works in Excel 2003 (& later with minor edit, see below). Press Ctrl+Shift+Enter (not just Enter) to enter this as an array formula.

=IF(ISBLANK(A65536),INDEX(A1:A65535,MAX((A1:A65535<>"")*(ROW(A1:A65535)))),A65536)

Be aware that Excel 2003 is unable to apply an array formula to an entire column. Doing so yields #NUM!; unpredictable results may occur! (EDIT: Conflicting information from Microsoft: The same may or may not be true about Excel 2007; problem may have been fixed in 2010.)

That's why I apply the array formula to range A1:A65535 and give special treatment to the last cell, which is A65536 in Excel 2003. Can't just say A:A or even A1:A65536 as the latter automatically reverts to A:A.

If you're absolutely sure A65536 is blank, then you can skip the IF part:

=INDEX(A1:A65535,MAX((A1:A65535<>"")*(ROW(A1:A65535))))

Note that if you're using Excel 2007 or 2010, the last row number is 1048576 not 65536, so adjust the above as appropriate.

If there are no blank cells in the middle of your data, then I would just use the simpler formula, =INDEX(A:A,COUNTA(A:A)).

Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
9

An alternative solution without array formulas, possibly more robust than that of a previous answer with a (hint to a) solution without array formulas, is

=INDEX(A:A,INDEX(MAX(($A:$A<>"")*(ROW(A:A))),0))

See this answer as an example. Kudos to Brad and barry houdini, who helped solving this question.

Possible reasons for preferring a non-array formula are given in:

  1. An official Microsoft page (look for "Disadvantages of using array formulas").
    Array formulas can seem magical, but they also have some disadvantages:

    • You may occasionally forget to press CTRL+SHIFT+ENTER. Remember to press this key combination whenever you enter or edit an array formula.
    • Other users may not understand your formulas. Array formulas are relatively undocumented, so if other people need to modify your workbooks, you should either avoid array formulas or make sure those users understand how to change them.
    • Depending on the processing speed and memory of your computer, large array formulas can slow down calculations.
  2. Array Formula Heresy.

Community
  • 1
  • 1
  • I can't get this to work with the D column. I replaced all `A` occurrences with a `D`. What am I doing wrong? – Marc.2377 Sep 27 '19 at 20:55
6

if you search in Column (A) use :

=INDIRECT("A" & SUMPRODUCT(MAX((A:A<>"")*(ROW(A:A)))))

if your range is A1:A10 you can use:

=INDIRECT("A" & SUMPRODUCT(MAX(($A$1:$A10<>"")*(ROW($A$1:$A10)))))

in this formula :

SUMPRODUCT(MAX(($A$1:$A10<>"")*(ROW($A$1:$A10))))

returns last non blank row number ,and indirect() returns cell value.

Sharif Lotfi
  • 544
  • 6
  • 13
3

=INDEX(A:A, COUNTA(A:A), 1) taken from here

HattrickNZ
  • 4,373
  • 15
  • 54
  • 98
3

=MATCH("*";A1:A10;-1) for textual data

=MATCH(0;A1:A10;-1) for numerical data

aucuparia
  • 2,021
  • 20
  • 27
liharvi
  • 31
  • 1
2

Ive tried all the non-volatile versions but Not one version given above has worked.. excel 2003/2007update. Surely this can be done in excel 2003. Not as an array nor standard formula. I either get just a blank, 0 or #value error. So I resort to the volatile methods .. This worked..

=LOOKUP(2,1/(T4:T369<>""),T4:T369)

@Julian Kroné .. Using ";" instead of "," does NOT work! I think you are using Libre Office not MS excel? LOOKUP is so annoyingly volitile I use it as a last resort only

remote one
  • 111
  • 1
  • 2
1

For Microsoft office 2013

"Last but one" of a non empty row:

=OFFSET(Sheet5!$C$1,COUNTA(Sheet5!$C:$C)-2,0)

"Last" non empty row:

=OFFSET(Sheet5!$C$1,COUNTA(Sheet5!$C:$C)-1,0)
aprodan
  • 559
  • 5
  • 17
1

Microsoft 365:

=XLOOKUP(FALSE,ISBLANK(A:A),A:A,"",,-1)

par
  • 17,361
  • 4
  • 65
  • 80
0

Place this code in a VBA module. Save. Under functions, User defined look for This function.

Function LastNonBlankCell(Range As Excel.Range) As Variant
    Application.Volatile
    LastNonBlankCell = Range.End(xlDown).Value
End Function
0

I used HLOOKUP

A1 has a date; A2:A8 has forecasts captured at different times, I want the latest

=Hlookup(a1,a1:a8,count(a2:a8)+1)

This uses a standard hlookup formula with the lookup array defined by the number of entries.

aucuparia
  • 2,021
  • 20
  • 27
Chris
  • 9
  • 1
0

for textual data:

EQUIV("";A1:A10;-1)

for numerical data:

EQUIV(0;A1:A10;-1)

This give you the relative index of the last non empty cell in the range selected (here A1:A10).

If you want to get the value, access it via INDIRECT after building -textually- the absolute cell reference, eg:

INDIRECT("A" & (nb_line_where_your_data_start + EQUIV(...) - 1))
VMAtm
  • 27,943
  • 17
  • 79
  • 125
Adel
  • 1
0

I had the same problem too. This formula also works equally well:-

=INDIRECT(CONCATENATE("$G$",(14+(COUNTA($G$14:$G$65535)-1))))

14 being the row number of the first row in the rows you want to count.

Chronic Clawtooth

0

If you know that there are not going to be empty cells in between, the fastest way is this.

=INDIRECT("O"&(COUNT(O:O,"<>""")))

It just counts the non-empty cells and refers to the appropriate cell.

It can be used for a specific range as well.

=INDIRECT("O"&(COUNT(O4:O34,"<>""")+3))

This returns the last non empty cell in the range O4:O34.

0

This formula worked with me for office 2010:

=LOOKUP(2;1/(A1:A100<>"");A1:A100)

A1: the first cell A100: refer to the last cell in comparing

Wahed
  • 1
  • 1
0

I think the response from W5ALIVE is closest to what I use to find the last row of data in a column. Assuming I am looking for the last row with data in Column A, though, I would use the following for the more generic lookup:

=MAX(IFERROR(MATCH("*",A:A,-1),0),IFERROR(MATCH(9.99999999999999E+307,A:A,1),0))

The first MATCH will find the last text cell and the second MATCH finds the last numeric cell. The IFERROR function returns zero if the first MATCH finds all numeric cells or if the second match finds all text cells.

Basically this is a slight variation of W5ALIVE's mixed text and number solution.

In testing the timing, this was significantly quicker than the equivalent LOOKUP variations.

To return the actual value of that last cell, I prefer to use indirect cell referencing like this:

=INDIRECT("A"&MAX(IFERROR(MATCH("*",A:A,-1),0),IFERROR(MATCH(9.99999999999999E+307,A:A,1),0)))

The method offered by sancho.s is perhaps a cleaner option, but I would modify the portion that finds the row number to this:

=INDEX(MAX((A:A<>"")*(ROW(A:A))),1)

the only difference being that the ",1" returns the first value while the ",0" returns the entire array of values (all but one of which are not needed). I still tend to prefer addressing the cell to the index function there, in other words, returning the cell value with:

=INDIRECT("A"&INDEX(MAX((A:A<>"")*(ROW(A:A))),1))

Great thread!

FocusWiz
  • 523
  • 1
  • 3
  • 10
0

If you are not afraid to use arrays, then the following is a very simple formula to solve the problem:

=SUM(IF(A:A<>"",1,0))

You must press CTRL + SHIFT + ENTER because this is an array formula.

0

INDEX returns a value by index position in an array and ROWS then is used to specify the last position of the array.

=LET(array,A1:A10,INDEX(array,ROWS(array)))

Also works for multiple columns when setting the parameter [column_num] of INDEX to 0:

=LET(array,A1:C10,INDEX(array,ROWS(array),0))
rxsen
  • 11
  • 2
0

We will use a combination of INDEX, MAX, COUNTA, and OFFSET functions.

Assuming you want to find the last non-empty value in column A, use the following formula:

=INDEX(A:A, MAX(ROW(A:A)*(A:A<>"")))

(A:A<>""): This creates an array of TRUE/FALSE values, where TRUE represents non-empty cells in column A.

ROW(A:A)*(A:A<>""): This multiplies the row numbers by the TRUE/FALSE array, giving an array of row numbers for non-empty cells and 0 for empty cells.

MAX(ROW(A:A)*(A:A<>"")): This finds the maximum row number from the array of row numbers, which corresponds to the last non-empty cell in column A.

INDEX(A:A, MAX(ROW(A:A)*(A:A<>""))): Finally, the INDEX function returns the value of the last non-empty cell in column A based on the maximum row number.

Make sure to adjust the column reference in the formula to match your actual data column. Replace all instances of "A:A" with the correct column letter (e.g., "B:B" for column B, "C:C" for column C, and so on).

Please try this formula, and it should give you the value of the last non-empty cell in the specified column. If it still doesn't work, please ensure that your data is contiguous (no empty rows within the range) and that you have data in the specified column.

ritusmart
  • 325
  • 1
  • 2
  • 11
-1

A simple one which works for me:

=F7-INDEX(A:A,COUNT(A:A))
nemo
  • 12,241
  • 3
  • 21
  • 26
-1

Okay, so I had the same issue as the asker, and tried both top answers. But only getting formula errors. Turned out that I needed to exchange the "," to ";" for the formulas to work. I am using XL 2007.

Example:

=LOOKUP(2;1/(A:A<>"");A:A)

or

=INDEX(A:A;MAX((A:A<>"")*(ROW(A:A))))
Julian Kroné
  • 179
  • 1
  • 13
-3

For version tracking (adding the letter v to the beginning of the number), I found this one to work well in Xcelsius (SAP Dashboards)

="v"&MAX(A2:A500)
Infinite Recursion
  • 6,511
  • 28
  • 39
  • 51
Mr. B
  • 1