This question is now answered elegantly, thanks to Chris Neilsen, see the answer below. It is the one I will use from now on. The solution reliably finds the last cell in
Best way I know to find "true Last Cell" is to use 2 steps:
UsedRange
(i.e. UsedRange.Cells.CountLarge
)CountA
(i.e. WorksheetFunction.CountA(Range)
), as it is fast, and works with Hidden / AutoFiltered / Grouped ranges.This takes some time, so I've written an optimized code for the second step. Then I found @Chris' code edited on Nov 30, 2019, and it looked similar, though I was wondering why so different. I compared (...did my best to do apple v apple), and was surprised by the results.
If my tests are reliable, then all what matters is how many searches you do with CountA
. I call it cycle - it is actually the number of CountA
functions!
My routine does up to 34 cycles, and @Chris' routine seems to do up to 32..80+ cycles. His code seems to test the same ranges repeatedly.
Please have a look at the test table Link, see my test results in VBA notes, and watch Immediate for your live results. You may test with any content, or even use an ActiveSheet in your own WorkBook. Play with parameters in VBA at "==== PARAMETERS TO BE CHANGED ====". You may zoom to 10%-15% to see painted cells showing the search ranges for each cycle. That's where the number of cycles becomes visible.
Note: I have not found any side-effects or errors with this so far. I avoid using Range.Find
, and changing its parameters behind the scenes. Some users will learn it the hard way... - like I did, when I then replaced text in the entire workbook, just to find it out days later.
Note2: This is my first post, please excuse possible glitches here.
Function GetLastSheetCellRng(ws As Excel.Worksheet) As Range
'Returns the [Range] of last used cell of the specified [Worksheet], located in the cross-section of the bottom row and right column with non-empty cells
Dim wf As Excel.WorksheetFunction: Set wf = Application.WorksheetFunction
Dim Xfound&, Yfound&, Xfirst&, Yfirst&, Xfrom&, Yfrom&, Xto&, Yto As Long
With ws
'1. step: UsedRange last cell
Set GetLastSheetCellRng = .UsedRange.Cells(.UsedRange.Cells.CountLarge) 'Getting UsedRange last cell
Yfound = GetLastSheetCellRng.Row: Xfound = GetLastSheetCellRng.Column
'2. step: Check non-empty cells in UsedRange last cell row & column
'If not found, then search up for last non-empty row, and search left for last non-empty column
If (wf.CountA(.Rows(Yfound)) = 0) And (Yfound > 1) Then
Yto = Yfound
Yfrom = Yto \ 2
Yfirst = 0
Do
If wf.CountA(.Range(.Rows(Yfrom), .Rows(Yto))) <> 0 Then
Yfirst = Yfrom
Yfrom = (Yfirst + Yto + 0.5) \ 2
Else
Yto = Yfrom - 1
Yfrom = (Yfrom + Yfirst) \ 2
End If
Loop Until Yfirst = Yfrom
If Yfirst = 0 Then
Yfound = 1 'If no cell found, then 1st row returned
Else
Yfound = Yfirst
End If
End If
If (wf.CountA(.Columns(Xfound)) = 0) And (Xfound > 1) Then
Xto = Xfound
Xfrom = Xto \ 2
Xfirst = 0
Do
If wf.CountA(.Range(.Columns(Xfrom), .Columns(Xto))) <> 0 Then
Xfirst = Xfrom
Xfrom = (Xfirst + Xto + 0.5) \ 2
Else
Xto = Xfrom - 1
Xfrom = (Xfrom + Xfirst) \ 2
End If
Loop Until Xfirst = Xfrom
If Xfirst = 0 Then
Xfound = 1 'If no cell found, then 1st column returned
Else
Xfound = Xfirst
End If
End If
Set GetLastSheetCellRng = .Cells(Yfound, Xfound)
End With
End Function