Change variable result each time used in sub vba - excel

I have code which sorts and copies results from one worksheet to another. Sometimes I need to paste copied range to the next blank cell on selected worksheet, for which I need to use ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row.
Worksheets("Wallets").AutoFilterMode = False
Worksheets("Wallets").Range("$A$1:$J10000").AutoFilter Field:=5, Criteria1:="*TRANSFER*"
Worksheets("Wallets").Range("$A$1:$J10000").AutoFilter Field:=7, Criteria1:=">0"
Worksheets("Wallets").Range("B2:I" & Worksheets("Wallets").Cells(Rows.Count, 1).End(xlUp).Row).Copy
Worksheets("Transfers").Cells(Worksheets("Transfers").Cells(Rows.Count, 1).End(xlUp).Row, 1).Offset(1, 0).PasteSpecial Paste:=xlPasteValue
Worksheets("Wallets").AutoFilterMode = False
Worksheets("Wallets").Range("$A$1:$J10000").AutoFilter Field:=5, Criteria1:="*EXCHANGE*"
Worksheets("Wallets").Range("$A$1:$J10000").AutoFilter Field:=7, Criteria1:=">0"
Worksheets("Wallets").Range("B2:I" & Worksheets("Wallets").Cells(Rows.Count, 1).End(xlUp).Row).Copy
Worksheets("Transfers").Cells(Worksheets("Transfers").Cells(Rows.Count, 1).End(xlUp).Row, 1).Offset(1, 0)..PasteSpecial Paste:=xlPasteValues
I was thinking about changing code so i can replace this part more easily if I need to use other column for some worksheets for example. Is there any way to make variable recalculate each time it used in sub? Part of code below just saves first result and uses it, but I need to update row count number it for each worksheet which is currently used(perferably without using Worksheets.Select).
Sub Sort_Wallets()
Dim x As Long
x = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
Worksheets("Wallets").AutoFilterMode = False
Worksheets("Wallets").Select
Worksheets("Wallets").Range("$A$1:$J10000").AutoFilter Field:=5, Criteria1:="*TRANSFER*"
Worksheets("Wallets").Range("$A$1:$J10000").AutoFilter Field:=7, Criteria1:=">0"
Worksheets("Wallets").Range("B2:I" & x).Copy
Worksheets("Transfers").Select
Worksheets("Transfers").Cells(x, 1).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
Worksheets("Wallets").AutoFilterMode = False
Worksheets("Wallets").Range("$A$1:$J10000").AutoFilter Field:=5, Criteria1:="*EXCHANGE*"
Worksheets("Wallets").Range("$A$1:$J10000").AutoFilter Field:=7, Criteria1:=">0"
Worksheets("Wallets").Range("B2:I" & x).Copy
Worksheets("Transfers").Cells(x, 1).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
End Sub

If you are looping through worksheets you can, but not if you are only using two worksheets. You will need to specify the worksheet. You are using x as the last row in each case and I doubt that is true. Why set J10000 if you are going to find the last row? Also, it looks like you only want to copy the visible cells after you filter. You need to specify that you only want the visible cells. It is easier to follow your code if you Set the variables, ranges and worksheets so as not to repeat long lines. Here is an example of what I just said using your code. There may even be a better solution, but this is more readable than what you have.
Sub Sort_Wallets()
Dim destlr As Long
Dim sourcelr As Long
Dim wk1 As Worksheet
Dim wk2 As Worksheet
Dim FiltRng As Range
Set wk1 = ThisWorkbook.Worksheets("Wallets")
Set wk2 = ThisWorkbook.Worksheets("Transfers")
destlr = wk2.Cells(Rows.Count, 1).End(xlUp).Row
sourcelr = wk1.Cells(Rows.Count, 1).End(xlUp).Row
Set FiltRng = wk1.Range(wk1.Cells(1, 1), wk1.Cells(sourcelr, 10))
wk1.AutoFilterMode = False
FiltRng.AutoFilter Field:=5, Criteria1:="*TRANSFER*"
FiltRng.AutoFilter Field:=7, Criteria1:=">0"
wk1.Range("B2:I" & sourcelr).SpecialCells(xlCellTypeVisible).Copy
wk2.Cells(destlr, 1).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
wk1.AutoFilterMode = False
FiltRng.AutoFilter Field:=5, Criteria1:="*EXCHANGE*"
FiltRng.AutoFilter Field:=7, Criteria1:=">0"
wk1.Range("B2:I" & sourcelr).SpecialCells(xlCellTypeVisible).Copy
wk2.Cells(destlr, 1).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
End Sub

Assuming you're using your worksheets like data tables, use "tables". For each table of data, highlight it and "Insert Table", and then go into the table ribbon (available only when the cursor is within that table) and change the name of your table from whatever it is ("Table5") to something that makes sense to you.
In VBA, these are called ListObjects. As long as you know the names of these tables, you can get them with the following code:
'Returns the specified object from a collection
'Returns Nothing if the value in the collection doesn't exist.
'Throws no errors
Private Function GetFromCollection(col As Collection, sKey As String) As Object
On Error Resume Next
Set GetFromCollection = col.item(sKey)
Err.Clear
End Function
Public Function GetListObjectFromWorkbook(sTableName As String, Optional bRecache As Boolean = False) As ListObject
Static bInitialized As Boolean
Static col As Collection
Dim lo As ListObject
Dim sht As Worksheet
If bRecache Or Not bInitialized Then
Set col = New Collection
For Each sht In Sheets
For Each lo In sht.ListObjects
col.Add lo, lo.Name
Next lo
Next sht
bInitialized = True
End If
Set GetListObjectFromWorkbook = GetFromCollection(col, sTableName)
End Function
From there, you don't need to know where the last row is! Adding a new row is:
Dim listrow As ListRow
Set listrow = GetListObjectFromWorkbook(sTableName).ListRows.Add
and you can manipulate the values of that new ListRow via listrow.Range
FYI: You can sort ListObjects, too. See the VB code in https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.tools.excel.listobject.sort?view=vsto-2017

Related

VBA autofilter not filtering table from array

I have an excel macro where I am trying to filter a table named "TrialBalance" based on the criteria loaded into an array. I can't figure out why it will only filter by the first value in the array.
I also need to reference the field name where the field is because the column location will change. I am a novice at excel vba, and have tried to use different code to get it to work, but it isn't working.
Sub CreatePremiumPvt()
Dim arr As Variant
Dim i As Integer, lrow As Long
lrow = Sheets("2a.Premium").Cells(Rows.Count, "M").End(xlUp).Row
arr = Sheets("2a.Premium").Range("M2:M" & lrow).Value
Application.ScreenUpdating = False
'Filter & Copy Premium Data from TB
Sheets("TrialBalance").ListObjects("TrialBalance").Range.AutoFilter Field:=31, Operator:=xlFilterValues, Criteria1:=arr
Sheets("TrialBalance").ListObjects("TrialBalance").Range.AutoFilter Field:=1, Criteria1:=Worksheets("Start Here").Range("C7").Value
'add code to copy & paste columns to Premium tab
Application.CalculateFull
Application.ScreenUpdating = True
End Sub
An array read from a range is not in a form that is recognised by AutoFilter: you need to transform it using (eg) Application.Transpose:
Sub CreatePremiumPvt()
Dim arr As Variant
With Worksheets("2a.Premium")
arr = .Range("M2:M" & .Cells(Rows.Count, "M").End(xlUp).Row).Value
End With
Application.ScreenUpdating = False
With Sheets("TrialBalance").ListObjects("TrialBalance")
.Range.AutoFilter Field:=31, Operator:=xlFilterValues, _
Criteria1:=Application.Transpose(arr)
.Range.AutoFilter Field:=1, Criteria1:=Worksheets("Start Here").Range("C7").Value
End With
Application.CalculateFull
Application.ScreenUpdating = True
End Sub

autofilter erroring out

I want to create an autofilter macro for an excel sheet, that will filter out any rows that do not contain "ballroom*" in Column E, but will also leave any rows where Column E is empty
Have basic programming knowledge, have taught myself what I know thus far in VBA
This is what I have currently
Sub row_deleter()
Dim ws As Worksheet
Dim rng As Range
Dim lastrow As Long
''setting varibles
Set ws = ActiveSheet
lastrow = ws.Range("E" & ws.Rows.count).End(xlUp).Row
Set rng = ws.Range("E1:E" & lastrow)
''actual filter function
With rng
.AutoFilter field:=5, Criteria1:=IsEmpty(rng), Operator:=xlAnd, Criteria2:="=*ballroom*"
.SpecialCells(xlCellTypeVisible).EntireRow.delete
End With
''turn off filters
ws.AutoFilterMode = False
End Sub
When I try to run this code it gives me a 1004 error saying `AutoFilter` method of range class failed, and the debug points to the `AutoFilter` line. Have tried a few things thus far with syntax etc and nothing seems to be working.
First, let's make sure that your table has an AutoFilter. Additionally, your criteria shouldn't be relevant to any range, just what's being filtered. Also, I believe your criteria should be xlOr - a cell can't be blank AND have ballroom in it. Try this:
Sub row_deleter()
Dim ws As Worksheet
Dim rng As Range
Dim lastrow As Long
''setting varibles
Set ws = ActiveSheet
lastrow = ws.Range("E" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("E1:E" & lastrow)
''turn on autofilter if it's off
If ws.AutoFilterMode = False Then
ws.UsedRange.AutoFilter
End If
''actual filter function
With rng
.AutoFilter Field:=1, Criteria1:="=", Operator:=xlOr, Criteria2:="=*ballroom*"
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
''turn off filters
ws.AutoFilterMode = False
End Sub
field:= is an offset and you only have a single column as your range. You want that to be field:=1
You are also using xland you want xlor. Can't have both an empty cell and a cell with ballroom.
.AutoFilter Field:=1, Criteria1:=IsEmpty(rng), Operator:=xlOr, Criteria2:="=*ballroom*"
Thought you have your answer and because of your comment (you want to delete every row which doesn't match your criteria) I adjusted your code to make it easier to read and perform what you actually want it to:
Option Explicit
Sub row_deleter()
Dim lastrow As Long
''setting varibles
'you can use a With ActiveSheet and avoid the use of ws Thought I wouldn't recommend using ActiveSheet unless you attach
'this macro to a button on the sheet itself.
With ActiveSheet
lastrow = .Range("E" & .Rows.Count).End(xlUp).Row
''actual filter function
.UsedRange.AutoFilter Field:=5, Criteria1:="<>", Operator:=xlOr, Criteria2:="<>*ballroom*"
.Range("A2:A" & lastrow).SpecialCells(xlCellTypeVisible).EntireRow.Delete
''turn off filters
.AutoFilterMode = False
End With
End Sub

Adding AutoFilter Criteria one by one

I would like to add AutoFilter Criteria to my excel table in separate Subs.
What I have at the moment looks a little something like this
.AutoFilter Field:=deviceTypeColumnId, Criteria1:=[dScenarioIndependent], Operator:=xlOr, _
Criteria2:=[dSmartphoneDeviceType]
What I would like to have is a method to first filter by Criteria1, and then, in another Sub, add Criteria2 to the existing AutoFilter. To my mind, it should look something like this:
Sub firstSub
.AutoFilter Field:=deviceTypeColumnId, Criteria1:=[dScenarioIndependent]
end sub
Sub secondSub
.AutoFilter mode:=xlAddCriteria, Field:=deviceTypeColumnId, Criteria1:=[dSmartphoneDeviceType]
'I know that mode doesn't exist, but is there anything like that?
end sub
Do you know any way to achieve this?
There isn't, that I know of, a way of "adding on" criteria to a filter which has previously been applied.
I have produced a work-around, which would work for what you are attempting to do. You will just have to add on scenarios to the select case statement, going up to the maximum number of filters which you will want to have.
EDIT: what it does; copy the filtered column to a new worksheet, and remove duplicates on that column. You're then left with the values which have been used to filter the column. Assign the values to an array, and then apply the number of elements of the array as a filter on the column, whilst including the new value you wish to filter on.
EDIT 2: added in a function to find the last row for when a table is already filtered (we want the last row, not the last visible row).
Option Explicit
Sub add_filter()
Dim wb As Workbook, ws As Worksheet, new_ws As Worksheet
Dim arrCriteria() As Variant, strCriteria As String
Dim num_elements As Integer
Dim lrow As Long, new_lrow As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets("data")
Application.ScreenUpdating = False
lrow = ws.Cells(Rows.Count, 1).End(xlUp).Row
ws.Range("A1:A" & lrow).Copy 'Copy column which you intend to add a filter to
Sheets.Add().Name = "filter_data"
Set new_ws = wb.Sheets("filter_data")
With new_ws
.Range("A1").PasteSpecial xlPasteValues
.Range("$A$1:$A$" & Cells(Rows.Count, 1).End(xlUp).Row).RemoveDuplicates _
Columns:=1, Header:=xlYes 'Shows what has been added to filter
new_lrow = Cells(Rows.Count, 1).End(xlUp).Row
If new_lrow = 2 Then
strCriteria = .Range("A2").Value 'If only 1 element then assign to string
Else
arrCriteria = .Range("A2:A" & Cells(Rows.Count, 1).End(xlUp).Row) 'If more than 1 element make array
End If
Application.DisplayAlerts = False
.Delete
Application.DisplayAlerts = True
End With
If new_lrow = 2 Then
num_elements = 1
Else
num_elements = UBound(arrCriteria, 1) 'Establish number elements in array
End If
lrow = last_row
Select Case num_elements
Case 1
ws.Range("$A$1:$A$" & lrow).AutoFilter 1, _
Array(strCriteria, "New Filter Value"), Operator:=xlFilterValues
Case 2
ws.Range("$A$1:$A$" & lrow).AutoFilter 1, _
Array(arrCriteria(1, 1), arrCriteria(2, 1), _
"New Filter Value"), Operator:=xlFilterValues
Case 3
ws.Range("$A$1:$A$" & lrow).AutoFilter 1, _
Array(arrCriteria(1, 1), arrCriteria(2, 1), _
arrCriteria(3, 1), "New Filter Value"), Operator:=xlFilterValues
End Select
Application.ScreenUpdating = True
End Sub
Function:
Function last_row() As Long
Dim rCol As Range
Dim lRow As Long
Set rCol = Intersect(ActiveSheet.UsedRange, Columns("A"))
lRow = rCol.Row + rCol.Rows.Count - 1
Do While Len(Range("A" & lRow).Value) = 0
lRow = lRow - 1
Loop
last_row = lRow
End Function
Hope this helps.

Copy values from one sheet into another using Filter option

I am trying to copy the values from one Excel sheet into another using Filter option. For example I have used only ten records, but in real time I am not sure the data that will be present. Also, I need to know the first cell value after a filter. For example, if I use filter the first value is reflecting as B4 and next time it is showing B6. I need to select that also dynamically using macro.
ActiveSheet.Range("$A$1:$BG$10").AutoFilter Field:=2, Criteria1:="2"
Range("B5:BG5").Select
The above code should be modified. Instead of $BG$10 it should be the number of rows, then Instead of B5:BG5 it must be the first cell after filter.
Try this:
Dim rngToFilter As Range
With ActiveSheet
.AutoFilterMode = False 'to make sure no filter is applied yet
Set rngToFilter = .Range("A1", .Range("BG" & Rows.Count).End(xlUp)) 'set the dynamic range
rngToFilter.AutoFilter Field:=2, Criteria1:="2" 'apply the filter
rngToFilter.Resize(.Range("BG" & Rows.Count).End(xlUp).Row - 1).Offset(1, 0).SpecialCells(xlCellTypeVisible).Select 'Offset 1 row to exclude the header, resize to select the first row only.
End With
Above code selects all the items that are filtered.
I you want so select only the 1st item filtered, then use below.
Sub Sample()
Dim rngToFilter As Range, rngFilter As Range
Dim i As Integer
With ActiveSheet
.AutoFilterMode = False 'to make sure no filter is applied yet
Set rngToFilter = .Range("A1", .Range("BG" & Rows.Count).End(xlUp)) 'set the dynamic range
rngToFilter.AutoFilter Field:=2, Criteria1:="2" 'apply the filter
Set rngFilter = rngToFilter.Resize(.Range("BG" & Rows.Count).End(xlUp).Row - 1).Offset(1, _
0).SpecialCells(xlCellTypeVisible)
rngToFilter.Resize(.Range("BG" & Rows.Count).End(xlUp).Row - _
(rngFilter.Cells.Count / rngFilter.Columns.Count)).Offset(1, _
0).SpecialCells(xlCellTypeVisible).Select
End With
End Sub
No error handler yet.
I leave it to you. :D
Try following code:
Sub test()
Dim lastRow As Long, firstVisibleRow As Long
ActiveSheet.AutoFilterMode = False
'find last non empty row number in column A'
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
'apply filter'
Range("$A$1:$BG$" & lastRow).AutoFilter Field:=2, Criteria1:="2"
On Error GoTo errHandler
'find first visible row number in the filtered range, if there is no rows matching the filter criteria, we'll get message from the MsgBox'
firstVisibleRow = Range("$A$2:$BG$" & lastRow).SpecialCells(xlCellTypeVisible).Row
On Error GoTo 0
'select range'
Range("B" & firstVisibleRow & ":BG" & firstVisibleRow).Select
Exit Sub
errHandler:
MsgBox "There is no rows matching the filter criteria"
End Sub

Excel 2007, Copying rows from one sheet to another based on a value in 1 column

I'm trying to copy a range of rows where the rows chosen are based on the value in one cell.I want to do this for all rows containing the same value in a cell, then move on to the next value an append to the bottom of the first list.
Below is my attempt at explaining what I wish to achieve - hopefully the above will help explain more my dilemma. I have looked around for this but not quite found what I want. I thought it would be simple and probably is.
I receive a data dump with thousands of rows of data and 18 columns. Based on the value of column P "Contract" I want to copy entire rows into a new single worksheet workingdata. Not all the data will go into the workingdata worksheet.
The contract numbers are c1234, c1235, c2345 etc.
What i am after achieving is copying and sorting, so copy all the rows of data where contract number is c1234, in workingdata, then directly below it copy all rows where contract is c1235 and so on.
I thought I could select the range P:P and sort but to no avail.
Sheets("Data Dump").Select
Columns("P:P").Select
If Selection.Value = "C1234" Then
Selection.EntireRow.copy
I know I should post what i have tried, but it would be a pathetic, for some reason I just can't seem to get my head round this one.
Here's my latest effort - I know there are errors
Dim oWorksheet As Excel.Worksheet
Dim oRangeSource As Excel.Range
Dim oRangeDest As Excel.Range
Set oWorksheet = Worksheets("DataDump")
Set oRangeSource = oWorksheet.Range("p:p")
Set oRangeDest = Worksheets("workingdata")
If oRangeSource="CA0004000" Then Select.EntireRow
Selection.EntireRow.copy
Sheets("workingdata").Select.Paste
End If
latest effort but does not sort data or get rid of unwanted, I have to do a manual filter and sort which sorts of defeats the object of the macro
Sub copy()
'
' copy Macro
'
Dim rngContracts As Range: Set rngContracts = Sheets("DataDump").Range("P:P")
Dim wsData As Worksheet
Dim wsFound As Boolean: wsFound = False
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.Name = "Working Data" Then
Set wsData = ws
wsFound = True
Exit For
End If
Next ws
If wsFound = False Then
Application.CutCopyMode = False
ActiveSheet.Range("A1").EntireRow.copy
Set wsData = Sheets.Add(After:=Sheets(Sheets.Count))
wsData.Name = "Working Data"
wsData.Range("A1").EntireRow.PasteSpecial xlPasteAll, Transpose:=False
End If
Dim iCell As Range
For Each iCell In rngContracts
If iCell.EntireRow.Hidden = False Then
Application.CutCopyMode = False
iCell.EntireRow.copy
wsData.Range("P" & Rows.Count).End(xlUp).Offset(1, 0).EntireRow.PasteSpecial xlPasteAll, Transpose:=False
End If
Next iCell
Application.CutCopyMode = False
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Latest attaempt - copies the data I need but does not sort:
Sub copytest()
'
' copytest Macro
'
Set MR = Sheets("data Dump").Range("P:P")
For Each cell In MR
If cell.Value = "CA000154" Then
cell.EntireRow.copy
Sheets("working data").Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial
End If
If cell.Value = "CA000220" Then
cell.EntireRow.copy
Sheets("working data").Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial
End If
If cell.Value = "CA000393" Then
cell.EntireRow.copy
Sheets("working data").Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial
End If
If cell.Value = "CA000429" Then
cell.EntireRow.copy
Sheets("working data").Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial
End If
Application.CutCopyMode = False
Next
End Sub
Record a macro to set filters on your data select one filter only.
Then, edit the code and loop through each filter copying the visible range on to your sheet. This must also sort your data as the filters are already sorted.
Also, take a look at creating filter arrays in the Excel VBA help with regards to using them to sort.

Resources