Find last column assigned in Range - excel

I need to find out the column that is the last column in a range that is defined with:
Set RngSource = ActiveWorkbook.ActiveSheet.UsedRange

First things first
Never use UsedRange to set your range. I have explained it HERE as to why you shouldn't use UsedRange
Set RngSource = ActiveWorkbook.ActiveSheet.UsedRange
Find the Last Column that has data and the Last Row which has data and then set your range. So your question of finding the last column from the range will not arise. Here is an example
Sub Sample()
Dim ws As Worksheet
Dim rng As Range
Dim lRow As Long, lCol As Long
'~~> Set your worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
'~~> Find Last Row
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find Last Column
lCol = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
Else
lRow = 1: lCol = 1
End If
'~~> Set your range
Set rng = .Range(.Cells(1, 1), .Cells(lRow, lCol))
Debug.Print rng.Address
End With
End Sub

Use End(xlToRight) with an activecell.

Related

Convert Range to a table

I have an Excel spreadsheet with the tabs "Analysis" and "Database". I want to create a button in Analysis that when clicked will convert the Database tab into a table. The Database isn't static and different users are always adding data.
I have the code below but it fails at the ".parent..." line of code.
Sub Convert_Table()
With ThisWorkbook.Sheets("Database").Range("a1")
.Parent.ListObjects.Add(xlSrcRange, ThisWorkbook.Sheets("Database").Range(.End(xlDown), .End(xlToRight)), , xlYes).Name = "Table1"
End With
End Sub
ThisWorkbook.Sheets("Database").Range("a1").Parent is the Sheets("Database"). Simplify your code.
I would do this slightly different.
I will find the last row and last column to make my range and then create the table. xlDown and xlToRight are not reliable if there are blank cells in between.
Is this what you are trying (UNTESTED)? I have commented the code but if you still have a problem understanding it, simply post a comment below.
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lastRow As Long
Dim lastCol As Long
Dim rng As Range
Dim tbl As ListObject
'~~> This is your worksheet
Set ws = ThisWorkbook.Sheets("Database")
With ws
'~~> Unlist the previously created table
For Each tbl In .ListObjects
tbl.Unlist
Next tbl
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
'~~> Find last row
lastRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find last column
lastCol = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
'~~> Set your rnage
Set rng = .Range(.Cells(1, 1), .Cells(lastRow, lastCol))
'~~> Create the table
.ListObjects.Add(xlSrcRange, rng, , xlYes).Name = "Table1"
End If
End With
End Sub

Selecting a range until the last used row

I am trying to select a range until the last used row in the sheet. I currently have the following:
Sub Select_Active_Down()
Dim lr As Long
lr = ActiveSheet.UsedRange.Rows.Count
If Cells(ActiveCell.Row, ActiveCell.Column) = Cells(lr, ActiveCell.Column) Then
MsgBox "There isn't any data to select."
Else
Range(Cells(ActiveCell.Row, ActiveCell.Column), Cells(lr, ActiveCell.Column)).Select
Cells(lr, ActiveCell.Column).Activate
End If
End Sub
The issue is that I need to select multiple columns, and this will only select the first column of the active range. How can I modify this to select multiple columns rather than just the first?
What about selection the entire region? This can be done as follows in VBA:
Selection.CurrentRegion.Select
There also is the possibility to select the entire array. For that, just press Ctrl+G, choose Special and see over there.
I would do this slightly different. I would use .Find to find the last row and the last column (using the same logic shown in the link) to construct my range rather than using Selection | Select | ActiveCell | UsedRange | ActiveSheet.
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim LastRow As Long
Dim LastColumn As Long
Dim rng As Range
'~~> Change it to the relevant sheet
Set ws = Sheet1
With ws
'~~> Check if there is data
If Application.WorksheetFunction.CountA(.Cells) = 0 Then
MsgBox "No Data Found"
Exit Sub
End If
'~~> Find last row
LastRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find last column
LastColumn = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
'~~> Construct your range
Set rng = .Range(.Cells(1, 1), .Cells(LastRow, LastColumn))
'~~> Work with the range
With rng
MsgBox .Address
'
'~~> Do what you want with the range here
'
End With
End With
End Sub

Trying to find value in range and get its row. Variable not set error?

The code below is supposed to take the value for net in each month, copies it, search for net name in range1(another worksheet) and pastes value in the cell corresponding to that row and column "AA".
This part of code is having issue:
Set Netrng = Range("AA" & Range1.Find(What:=Net, After:=ActiveCell, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Rows.row)
The error is -
object variable or with block variable not set.
what am I doing wrong?
Sub test()
Dim Range2 As Range
Dim lRow As Long
Dim Count As Long
Dim Net As String
Dim Line As Range
Dim Netrng As Range
Dim First As Range
Dim Range1 As Range
Dim wb As Worksheet
Set First = ActiveCell
Set wb = ActiveSheet
Set Range1 = wb.Range(First, First.End(xlDown))
ActiveWindow.ActivatePrevious
ActiveSheet.PivotTables("PivotTable1").PivotFields("Client Code").CurrentPage _
= "BUN"
ActiveSheet.Range("B5").Activate
lRow = Cells(Rows.Count, 1).End(xlUp).row - 6
Set Range2 = Range(ActiveCell.Offset(2, -1), ActiveCell.Offset(lRow, -1))
Set Months = Range(ActiveCell.Offset(0, 0), ActiveCell.Offset(0, 2))
Count = 1
While Count <= Range2.Count
Set Line = Range2.Rows(Count)
Net = Line.Value
Line.Offset(0, 1).Copy
ActiveWindow.ActivatePrevious
Set Netrng = Range("AA" & Range1.Find(What:=Net, After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).row)
Netrng.Offset(0, 4).PasteSpecial Paste:=xlPasteValues
Netrng.Value = 0
ActiveWindow.ActivatePrevious
Line.Offset(0, 2).Copy
ActiveWindow.ActivatePrevious
Netrng.Offset(0, 8).PasteSpecial Paste:=xlPasteValues
ActiveWindow.ActivatePrevious
Count = Count + 1
Wend
End Sub
As is, the code is assuming that the Find is successful, which may not always be the case.
To test:
Dim foundRng as Range
Set foundRng = Range1.Find(What:=Net, After:=ActiveCell, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not FoundRng is Nothing Then
Set Netrng = Range("AA" & foundRng.Row)
...
End If
Other recommendations:
Avoid using Select and Activate. (and ActiveCell, ActiveWindow, anything Active).
Fully qualify which Workbook and Worksheet each Range is on (helpful reading in the answer on avoiding Select).
While...Wend is old-fashioned. Use a For Each loop.

Range Selection Method Dilemma

I need more experienced persons advice on some situation which I am facing. I have a test sample data in the following table.
hello good day today
hello good day today
hello good day today
hello good day today
hello good day today
hello good day today
hello good day today
today
today
I have used 4 ways to determine range as per following code and also the last cell by FIND method.
Sub test()
Dim ws As Worksheet
Dim myRange As Range
Dim myRange1 As Range
Dim myRange2 As Range
Dim rLastCell As Range
Set ws = ThisWorkbook.ActiveSheet
With ws
Set myRange = .Range(.Cells(1, 1), .Range("A1").SpecialCells(xlCellTypeLastCell))
Debug.Print ws.Name, myRange.Address
'set range with used area
Set myRange1 = ws.UsedRange
Debug.Print ws.Name, myRange1.Address
'set range with currentegion
Set myRange2 = .Range("A1").CurrentRegion
Debug.Print ws.Name, myRange2.Address
' finding lastcell and then set range
Set rLastCell = ActiveSheet.Cells.Find(What:="*", After:=.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)
Debug.Print rLastCell.Address
End With
End Sub
Results of Debug.Print are
Book1 $A$1:$D$11
Book1 $A$1:$D$11
Book1 $A$1:$D$6
$D$11
My specific query is that by UsedRange, SpecialCells(xlCellTypeLastCell) and FIND method I get the same results. Though use of these or other approaches depend on the situation at hand but considering this particular data situation, Is over-riding preference for a particular method is warranted.
EDIT:
Based on comments of #VBasic2008 and an excellent article referred by # QHarr , I am inclined to adopt for range determination in general situations following methodology. I would like to find Last Row and Last Column of the range utilizing the Last function suggested in Ron de Bruin Aricle . Range will be set based on Anchor Cell and Last Row and Last Column Values.Code followed by me as follows.
Sub Range_Detrmine()
Dim ws As Worksheet
Dim LastRow As Long
Dim LastCol As Long
Dim rng As Range
Dim Frng As Range
Set ws = ThisWorkbook.ActiveSheet
With ws
' Use all cells on the sheet
Set rng = .Cells
' Find the last row
LastRow = Last(1, rng)
LastCol = Last(2, rng)
Set Frng = .Range(.Cells(1, 1), .Cells(LastRow, LastCol))
Debug.Print LastRow & ":"; LastCol
Debug.Print ws.Name, Frng.Address
End With
End Sub
Function Last(choice As Long, rng As Range)
'Ron de Bruin, 5 May 2008
' 1 = last row
' 2 = last column
' 3 = last cell
Dim lrw As Long
Dim lcol As Long
Select Case choice
Case 1:
On Error Resume Next
Last = rng.Find(What:="*", _
After:=rng.Cells(1), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
Case 2:
On Error Resume Next
Last = rng.Find(What:="*", _
After:=rng.Cells(1), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
Case 3:
On Error Resume Next
lrw = rng.Find(What:="*", _
After:=rng.Cells(1), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
On Error Resume Next
lcol = rng.Find(What:="*", _
After:=rng.Cells(1), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
On Error GoTo 0
On Error Resume Next
Last = rng.Parent.Cells(lrw, lcol).Address(False, False)
If Err.Number > 0 Then
Last = rng.Cells(1).Address(False, False)
Err.Clear
End If
On Error GoTo 0
End Select
End Function
I tested the above code for some example situations as per snapshots appended. I tested the correctness of range determined before deleting formatted row and after deleting the formatted row.Also in case of filter applied it is giving correct range even though filter check-marks are visible in Header Row in Column H
Debug.Print LastRow & ":"; LastCol
Debug.Print ws.Name, Frng.Address
Results before and after range modification are :
17: 8
Sheet1 $A$1:$H$17
14: 7
Sheet2 $A$1:$G$14
I would like to know if there are some caveats to this approach.

Apply Dynamic Range in ActiveSheet.Range

I have run a macro to paste, edit, alter and split data using a specified data set.
On a new data set (more data) I ran into a problem with my data range.
ActiveSheet.Range("$A$1:$T$299").AutoFilter Field:=6, Criteria1:= _
"=Site Reference A", Operator:=xlOr, Criteria2:= _
"=Site Reference A Total"
Range("A1:T299").Select
Range("F160").Activate
Selection.SpecialCells(xlCellTypeVisible).Select
Selection.Copy
My data range is $A$1:$T$299. How would I make this dynamic?
Example -
Data set A has 200 columns.
Data set B has 230 columns.
A little long, but this full code is the safer way to find the last row and last column in a sheet.
Dim Sht As Worksheet
Dim FiltRng As Range
Dim LastCol As Long
Dim LastRow As Long
Set Sht = ActiveSheet '<-- better not rely on ActiveSheet
LastRow = FindLastRow(Sht)
LastCol = FindLastCol(Sht)
With Sht
Set FiltRng = .Range(.Cells(1, 1), .Cells(LastRow, LastCol)) ' <-- set the filtered range dynamically
End With
With Sht
FiltRng.AutoFilter Field:=6, Criteria1:= _
"=Site Reference A", Operator:=xlOr, Criteria2:= _
"=Site Reference A Total"
' rest of your code goes here
End With
'==========================================================
Function FindLastCol(Sht As Worksheet) As Long
' This Function finds the last col in a worksheet, and returns the column number
Dim LastCell As Range
With Sht
Set LastCell = .Cells.Find(What:="*", After:=.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
If Not LastCell Is Nothing Then
FindLastCol = LastCell.Column
Else
MsgBox "Error! worksheet is empty", vbCritical
End
End If
End With
End Function
'==========================================================
Function FindLastRow(Sht As Worksheet) As Long
' This Function finds the last row in a worksheet, and returns the row number
Dim LastCell As Range
With Sht
Set LastCell = .Cells.Find(What:="*", After:=.Cells(1), Lookat:=xlPart, LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)
If Not LastCell Is Nothing Then
FindLastRow = LastCell.Row
Else
MsgBox "Error! worksheet is empty", vbCritical
End
End If
End With
End Function
If there is no other data in column A, you can use a dynamic named range. Just use the following formula as the range for your named range and then reference the named range in your code. Keep in mind that this only works if there is no other data in column A.
=OFFSET($A$1,0,0,COUNTA($A:$A),1)

Resources