When I want to find the last used cell value, I use:
Dim LastRow As Long
LastRow = Range("E4:E48").End(xlDown).Row
Debug.Print LastRow
I am getting the wrong output when I put a single element into a cell. But when I put more than one value into the cell, the output is correct. What's the reason behind this?
Here's my two cents.
IMHO the risk of a hidden row with data being excluded is too significant to let
xlUp
be considered a One stop answer. I agree it's simple and will work MOST of the time, but it presents the risk of understating the last row, without any warning. This could produce CATASTROPHIC results at some poinit for someone who jumped on Stack Overlow and was looking to "sure way" to capture this value.The
Find
method is flawless and I would approve of it as a One Stop Answer. However the drawback of changing theFind
settings can be annoying, particularly if this is part of a UDF.The other answers posted are okay, however the complexity gets a little excessive. Thus here's my attempt to find a balance of reliability, minimal complexity, and not using
Find
.Why this is good:
Find
settingsWhy this is bad:
However, I think a One-Stop-Solution that has a drawback of messing up
find
settings or performing slower is a better overall solution. A user can then tinker with their settings to try to improve, knowing what's going on with their code. UsingxLUp
will not warn of the potential risks and they could carry on for who knows how long not knowing their code was not working correctly.Since the original question is about problems with finding the last cell, in this answer I will list the various ways you can get unexpected results; see my answer to "How can I find last row that contains data in the Excel sheet with a macro?" for my take on solving this.
I'll start by expanding on the answer by sancho.s and the comment by GlennFromIowa, adding even more detail:
Other things you might want to consider:
With that in mind, let's see how the common ways of getting the "last cell" can produce unexpected results:
.End(xlDown)
code from the question will break most easily (e.g. with a single non-empty cell or when there are blank cells in between) for the reasons explained in the answer by Siddharth Rout here (search for "xlDown is equally unreliable.")NOTE: I intend to make this a "one stop post" where you can use the
Correct
way to find the last row. This will also cover the best practices to follow when finding the last row. And hence I will keep on updating it whenever I come across a new scenario/information.Unreliable ways of finding the last row
Some of the most common ways of finding last row which are highly unreliable and hence should never be used.
UsedRange
should NEVER be used to find the last cell which has data. It is highly unreliable. Try this experiment.Type something in cell
A5
. Now when you calculate the last row with any of the methods given below, it will give you 5. Now color the cellA10
red. If you now use the any of the below code, you will still get 5. If you useUsedrange.Rows.Count
what do you get? It won't be 5.Here is a scenario to show how
UsedRange
works.xlDown
is equally unreliable.Consider this code
What would happen if there was only one cell (
A1
) which had data? You will end up reaching the last row in the worksheet! It's like selecting cellA1
and then pressing End key and then pressing Down Arrow key. This will also give you unreliable results if there are blank cells in a range.CountA
is also unreliable because it will give you incorrect result if there are blank cells in between.And hence one should avoid the use of
UsedRange
,xlDown
andCountA
to find the last cell.Find Last Row in a Column
To find the last Row in Col E use this
If you notice that we have a
.
beforeRows.Count
. We often chose to ignore that. See THIS question on the possible error that you may get. I always advise using.
beforeRows.Count
andColumns.Count
. That question is a classic scenario where the code will fail because theRows.Count
returns65536
for Excel 2003 and earlier and1048576
for Excel 2007 and later. SimilarlyColumns.Count
returns256
and16384
, respectively.The above fact that Excel 2007+ has
1048576
rows also emphasizes on the fact that we should always declare the variable which will hold the row value asLong
instead ofInteger
else you will get anOverflow
error.Note that this approach will skip any hidden rows. Looking back at my screenshot above for column A, if row 8 were hidden, this approach would return
5
instead of8
.Find Last Row in a Sheet
To find the
Effective
last row in the sheet, use this. Notice the use ofApplication.WorksheetFunction.CountA(.Cells)
. This is required because if there are no cells with data in the worksheet then.Find
will give youRun Time Error 91: Object Variable or With block variable not set
Find Last Row in a Table (ListObject)
The same principles apply, for example to get the last row in the third column of a table:
However this question is seeking to find the last row using VBA, I think it would be good to include an array formula for worksheet function as this gets visited frequently:
You need to enter the formula without brackets and then hit Shift + Ctrl + Enter to make it an array formula.
This will give you address of last used cell in the column D.
I wonder that nobody has mentioned this, But the easiest way of getting the last used cell is:
This essentially returns the same cell that you get by Ctrl + End after selecting Cell
A1
.A word of caution: Excel keeps track of the most bottom-right cell that was ever used in a worksheet. So if for example you enter something in B3 and something else in H8 and then later on delete the contents of H8, pressing Ctrl + End will still take you to H8 cell. The above function will have the same behavior.
I created this one-stop function for determining the last row, column and cell, be it for data, formatted (grouped/commented/hidden) cells or conditional formatting.
Results look like this:
For more detailed results, some lines in the code can be uncommented:
One limitation exists - if there are tables in the sheet, results can become unreliable, so I decided to avoid running the code in this case: