Is it possible to specify dynamic range in auto filter syntax? - excel

I have written below code to apply auto filter. Code is working fine. However I have specified static range, here: *Range("A1", **"M"** & lastRow).AutoFilter* .
Is there a way to replace "M" with last existing column number in sheet2.
I have calculated last existing column *myCol = rngFound.Column - 1*. But not sure how to use it.
Please help !!!
My Code:
Sub testfilter1()
Dim lastRow As Long
Dim myCol As Long
Dim rngFound As Range
ThisWorkbook.Sheets("sheet2").Activate
lastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
Set rngFound = ActiveSheet.Rows(1).Find(What:="", LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False)
myCol = rngFound.Column - 1 ' this will give last used column
Rows("1:1").Select
Selection.AutoFilter
ActiveSheet.Range("A1", "M" & lastRow).AutoFilter Field:=4, Criteria1:="*somename*"
End Sub

You could use Cells() within Range():
...Range(Cells(1,1),Cells(lastRow,myCol))...
Cells(1,1) is the cell A1. The format is Cells([row],[column])
Edit: Just realized you could also use Range("A1",Cells(lastRow,myCol)). Personally, if I use Cells() I do it in both places in Range(), but that's personal preference and this other way should work for you too.

Related

VBA, find last used column in the whole sheet

I googled a lot and found a lot of different solutions, but I need to improve the one I'm using now.
I want to find the last used column in the sheet using the find method not to consider the deleted cells.
All I want is to get the last column used, including the one in the row of the starting cell. In the image below if I use my code it will give last column = 4, because in the 2nd row data stops at column 4. Why isn't it giving 5 (header column) as result?
Thank you!!
With ActiveSheet
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
findlastcol = .Cells.Find(What:="*", _
After:=.Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
Else
findlastcol = 1
End If
End With
Example Table screenshot
+---------+---------+---------+---------+---------+
| Header1 | Header2 | Header3 | Header4 | Header5 |
+---------+---------+---------+---------+---------+
| Data | Data | Data | Data | |
+---------+---------+---------+---------+---------+
AutoFilter Kicks the Find Method
The Find method with xlFormulas is pretty much 'bullet proof', unless there is a filter involved which is happening in your case.
The following example shows how to do it by turning the AutoFilter off, which is not quite what one wants. It also shows how there were three not needed arguments. Additionally it is a different approach which does not need CountA.
A proper solution would be to copy the current filter into a Filter object and then apply it later back. Here is an example of how to do it.
The Code
Sub testBulletProof()
Dim LastCol As Long
Dim rng As Range
With ActiveSheet
If .AutoFilterMode Then
.AutoFilterMode = False
End If
Set rng = .Cells.Find(What:="*", _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious)
End With
If Not rng Is Nothing Then
LastCol = rng.Column
Else
LastCol = 1
End If
Debug.Print LastCol
End Sub
Since you might know the row where the headers are and the data will not have more columns then the header does, you could use this:
The Code
Sub testFindInRow()
Dim LastCol As Long
Dim rng As Range
With ActiveSheet
Set rng = .Rows(1).Find(What:="*", _
LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
End With
If Not rng Is Nothing Then
LastCol = rng.Column
Else
LastCol = 1
End If
Debug.Print LastCol
End Sub
You could try the following code:
Sub FindLastColumn()
Dim iLastCol As Integer
ActiveSheet.UsedRange 'Refreshing used range (may need to save wb also)
iLastCol = ActiveSheet.UsedRange.Columns(ActiveSheet.UsedRange.Columns.Count).Column
End Sub
Alternatively, you can try:
findlastcol = Selection.SpecialCells(xlCellTypeLastCell).Column

Countering circular reference with SUM and OFFSET (VLOOKUP & VBA involved)

To give you a breakdown of my spreadsheet:
I have a master spreadsheet that pulls data from another spreadsheet (generated daily), placing it into the next empty column and converting the column that previously held the formula to values. This is achieved with a combination of the following formula and VBA code:
=IF(ISNA(VLOOKUP("Row 1",'N:\Reports\[data.xls]Sheet1'!$A$2:$B$40,2,FALSE)),0,(VLOOKUP("Row 1",'N:\Reports\[data.xls]Sheet1'!$A$2:$B$40,2,FALSE)))
Sub Test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rLastCell As Range
Dim LastCol As Integer
Set rLastCell = ws.Cells.Find(what:="*", After:=ws.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
LastCol = rLastCell.Column
ws.Columns(LastCol).Copy ws.Columns(LastCol + 1)
With ws.Columns(LastCol)
.Copy .Offset(0, 1)
.Value = .Value
End With
End Sub
The intention is for Column B to be a 'totals' column, that dynamically sums all of the values in the relevant row as new entries are pulled by the formula/VBA combo and added to the first blank column. Unfortunately though, I also need to subtract that row's total from the value that the formula returns--however, doing so creates a circular reference.
My solution was to just exclude the last cell in the row (that has the formula) from the total, with this:
=SUM(C2:OFFSET(I$2,0,-1))
However, the dynamic range doesn't appear all that dynamic. It doesn't expand to include the next column when a new record is added, and I'm really not enough of a hand at this stuff to figure out why or how to rectify it.
Thanks in advance for any assistance with this and please don't hesitate to ask for any clarification!
It may be simplest to use a named range. If you name the column before the monthly total say LastDay, you can use:
=SUM(C2:INDEX(LastDay,ROW())
as your formula, and your code then becomes:
Sub Test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim rLastCell As Range
Dim LastCol As Integer
Set rLastCell = ws.Cells.Find(what:="*", After:=ws.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
LastCol = rLastCell.Column
With ws.Columns(LastCol)
.Copy .Offset(0, 1)
.Value = .Value
.Name = "LastDay"
End With
End Sub
Assuming I have understood your layout correctly.

Not getting values of all rows after auto filtering with for loop

I am struggling for writing the code - below query please help any one on writing it.
TestDataSheetName = ActiveWorkbook.Worksheets(x).Name
ActiveWorkbook.Worksheets(x).Activate
CountTestData = ActiveWorkbook.Worksheets(x).Range("A" & Rows.Count).End(xlUp).Row
Range("A10").Select
Range("A10").AutoFilter
Selection.AutoFilter Field:=14, Criteria1:=">=" & DateToday
ActiveWorkbook.Worksheets(x).Activate
CountTestDataAftFilter = ActiveWorkbook.Worksheets(x).Range("A1", Range("A65536").End(xlUp)).SpecialCells(xlCellTypeVisible).Count
MsgBox CountTestDataAftFilter
For w = 10 To CountTestDataAftFilter
Set Foundcell1 = ActiveWorkbook.Worksheets(x).Cells.Find(What:=DateToday, After:=[ActiveCell], _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
Next
' after filtering with today's date i got 5 rows with today's date and i have written for loop for getting all row values but after finding first row then it is not finding the second row value and it is again start with first row
Please help me on above code.
Thanks&Regards,
Basha
You're looking for the .FindNext function. Try something like this: (Please note, you may need to modify this code slightly to fit your particular case.)
Sub UseFindNext()
Dim TestDataSheet As Worksheet
Dim FoundCell1 As Range
Dim DateToday As Date
Dim firstAddress As String
Dim x As Long
Dim CountTestData As Long
Dim CountTestDataAftFilter As Long
x = 1
Set TestDataSheet = ActiveWorkbook.Worksheets(x)
CountTestData = TestDataSheet.Range("A" & Rows.count).End(xlUp).Row
Range("A10").AutoFilter Field:=14, Criteria1:=">=" & DateToday
CountTestDataAftFilter = TestDataSheet.Range("A1", Rows.count).End(xlUp)).SpecialCells(xlCellTypeVisible).count
Set FoundCell1 = TestDataSheet.Cells.Find(What:=DateToday, After:=TestDataSheet.Range("A10"), _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
LookIn:=xlValues, LookAt:=xlPart, MatchCase:=True)
firstAddress = FoundCell1.Address
Do
'Do whatever you're looking to do with each cell here. For example:
Debug.Print FoundCell1.Value
Loop While Not FoundCell1 Is Nothing And FoundCell1.Address <> firstAddress
End Sub
I don't know why you have to go through each value.
You already used AutoFilter to get the data you want.
But here's another approach that might work for you.
Sub test()
Dim ws As Worksheet
Dim wb As Workbook
Dim DateToday As String 'i declared it as string for the filtering
Dim rng, cel As Range
Dim lrow As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets(x)
DateToday = "Put here whatever data you want" 'put value on your variable
With ws
lrow = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("N10:N" & lrow).AutoFilter Field:=1, Criteria1:=DateToday
'I used offset here based on the assumption that your data has headers.
Set rng = .Range("N10:N" & lrow).Offset(1, 0).SpecialCells(xlCellTypeVisible)
'here you can manipulate the each cell values of the currently filtered range
For Each cel In rng
cel.EntireRow 'use .EntireRow to get all the data in the row and do your stuff
Next cel
.AutoFilterMode = False
End With
End Sub
BTW, this is based on this post which you might want to check as well to improve coding.
It is a good read. Hope this helps.

VBA Excel: Paste into Matching Row between Workbooks

I am trying to develop code to do the following:
1)Copy cells K4: M4 in Workbook 1, Sheet 1 <- I can do this step;
2)Find a cell in Workbook2, Sheet1, column C that matches cell B4 in
Workbook1, Sheet1;
3)Paste the copied values in columns P:R of the matching row in
Workbook 2, Sheet 1 as determined in Step 2.
My apologies in advance for being unable to advance my own work beyond step 1. I am, as I said, completely new to this and have scoured the web for an answer/learnings up until this point, without turning up a solution.
I tested this and it worked. Does this help you get started?
Sub CopyToMatchedRow()
Dim copyRng As Range, matchVal As Variant, matchRng As Range, matchRow As Integer
Set copyRng = Worksheets("Sheet1").Range("K4:M4")
Set matchRng = Worksheets("Sheet2").Range("C:C")
matchVal = Worksheets("Sheet1").Range("B4")
matchRow = matchRng.Find(What:=matchVal, After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
copyRng.Copy Destination:=Worksheets("Sheet2").Range("P" & matchRow & ":R" & matchRow)
End Sub

How do I find the last column with data?

I've found this method for finding the last data containing row in a sheet:
ws.Range("A65536").End(xlUp).row
Is there a similar method for finding the last data containing column in a sheet?
Lots of ways to do this. The most reliable is find.
Dim rLastCell As Range
Set rLastCell = ws.Cells.Find(What:="*", After:=ws.Cells(1, 1), LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
MsgBox ("The last used column is: " & rLastCell.Column)
If you want to find the last column used in a particular row you can use:
Dim lColumn As Long
lColumn = ws.Cells(1, Columns.Count).End(xlToLeft).Column
Using used range (less reliable):
Dim lColumn As Long
lColumn = ws.UsedRange.Columns.Count
Using used range wont work if you have no data in column A. See here for another issue with used range:
See Here regarding resetting used range.
I know this is old, but I've tested this in many ways and it hasn't let me down yet, unless someone can tell me otherwise.
Row number
Row = ws.Cells.Find(What:="*", After:=[A1] , SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Column Letter
ColumnLetter = Split(ws.Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Cells.Address(1, 0), "$")(0)
Column Number
ColumnNumber = ws.Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Try using the code after you active the sheet:
Dim J as integer
J = ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell).Row
If you use Cells.SpecialCells(xlCellTypeLastCell).Row only, the problem will be that the xlCellTypeLastCell information will not be updated unless one do a "Save file" action. But use UsedRange will always update the information in realtime.
I think we can modify the UsedRange code from #Readify's answer above to get the last used column even if the starting columns are blank or not.
So this lColumn = ws.UsedRange.Columns.Count modified to
this lColumn = ws.UsedRange.Column + ws.UsedRange.Columns.Count - 1 will give reliable results always
?Sheet1.UsedRange.Column + Sheet1.UsedRange.Columns.Count - 1
Above line Yields 9 in the immediate window.
Here's something which might be useful. Selecting the entire column based on a row containing data, in this case i am using 5th row:
Dim lColumn As Long
lColumn = ActiveSheet.Cells(5, Columns.Count).End(xlToLeft).Column
MsgBox ("The last used column is: " & lColumn)
I have been using #Reafidy method/answer for a long time, but today I ran into an issue with the top row being merged cell from A1-->N1 and my function returning the "Last Column" as 1 not 14.
Here is my modified function now account for possibly merged cells:
Public Function Get_lRow(WS As Worksheet) As Integer
On Error Resume Next
If Not IsWorksheetEmpty(WS) Then
Get_lRow = WS.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Dim Cell As Range
For Each Cell In WS.UsedRange
If Cell.MergeCells Then
With Cell.MergeArea
If .Cells(.Cells.Count).Row > Get_lRow Then Get_lRow = .Cells(.Cells.Count).Row
End With
End If
Next Cell
Else
Get_lRow = 1
End If
End Function
Public Function Get_lCol(WS As Worksheet) As Integer
On Error Resume Next
If Not IsWorksheetEmpty(WS) Then
Get_lCol = WS.Cells.Find(What:="*", after:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Dim Cell As Range
For Each Cell In WS.UsedRange
If Cell.MergeCells Then
With Cell.MergeArea
If .Cells(.Cells.Count).Column > Get_lCol Then Get_lCol = .Cells(.Cells.Count).Column
End With
End If
Next Cell
Else
Get_lCol = 1
End If
End Function
Here's a simple option if your data starts in the first row.
MsgBox "Last Row: " + CStr(Application.WorksheetFunction.CountA(ActiveSheet.Cells(1).EntireRow))
It just uses CountA to count the number of columns with data in the entire row.
This has all sorts of scenarios where it won't work, such as if you have multiple tables sharing the top row, but for a few quick & easy things it works perfect.

Resources