Excel VBA loop until - vba

I am using the following if code to filter col A and col B by the value in F1
Once sorted I copy the the filtered values in col A and paste them under the range value.
Then I move on to the next range and repeat the filter using a different range value (in this case cell G1).
I need to repeat this from cell F1 through to cell AH1.
Can I use a loop to do this?
If Range("F1").Value <> "" Then
Selection.AutoFilter
ActiveSheet.Range("$A$2:$B" & LastRow).AutoFilter Field:=2, Criteria1:=Range("F1").Value
Columns("A:A").Select
Application.CutCopyMode = False
Selection.Copy
Range("F2").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Selection.AutoFilter
Range("A1").Select
End If
If Range("G1").Value <> "" Then
Selection.AutoFilter
ActiveSheet.Range("$A$2:$B" & LastRow).AutoFilter Field:=2, Criteria1:=Range("G1").Value
Columns("A:A").Select
Application.CutCopyMode = False
Selection.Copy
Range("G2").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Selection.AutoFilter
Range("A1").Select
End If

Try the loop below. I have refactored your code to not use ActiveSheet and Select statements. Instead I qualify all objects and methods to their parent object and work directly with the object. It will avoid many pitfalls and errors in expected versus actual results of the code.
Dim LastRow As Long
'assume LastRow already set
Dim ws As Worksheet
Set ws = Worksheets("Sheet1") 'change as needed
With ws
Dim cel As Range
For Each cel In .Range("F1:AH1")
If Len(cel) > 0 Then
ws.UsedRange.AutoFilter
.Range("A2:B" & LastRow).AutoFilter Field:=2, Criteria1:=cel.Value
.Range("A2:A" & LastRow).SpecialCells(xlCellTypeVisible).Copy Destination:=cel.Offset(1)
End If
Next
End With
See How to Avoid Select

Related

Filtering a column based on a value and copying the value from the corresponding value

This is a screenshot of my excel doc.
I want to apply filters based on values: Bimbo Mexico, Bimbo Canada and copy and paste the values(from column A & B) in a new sheet. I want to do this using macro as I am building a template for a client. Is there a way to do this? I know it can be done manually using filters manually but I want it to be based on a macro
I want the output like this:
I used recording macro and this is the macro I got,
Sub RecordedMacro()
'
' RecordedMacro Macro
'
' Keyboard Shortcut: Ctrl+l
'
Sheets("report").Select
Range("C1").Select
ActiveSheet.Range("$A$1:$S$1001").AutoFilter Field:=3, Criteria1:="Barcel"
Columns("L:L").Select
Selection.Copy
Sheets("SkuRounds").Select
Columns("S:S").Select
ActiveSheet.Paste
Sheets("report").Select
ActiveSheet.Range("$A$1:$S$1001").AutoFilter Field:=3, Criteria1:= _
"Bimbo Canada"
Columns("L:L").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("SkuRounds").Select
Columns("T:T").Select
ActiveSheet.Paste
Sheets("report").Select
ActiveSheet.Range("$A$1:$S$1001").AutoFilter Field:=3, Criteria1:= _
"Bimbo Latin Centro"
Columns("L:L").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("SkuRounds").Select
Columns("U:U").Select
ActiveSheet.Paste
Sheets("report").Select
ActiveSheet.Range("$A$1:$S$1001").AutoFilter Field:=3, Criteria1:= _
"Bimbo México"
Columns("L:L").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("SkuRounds").Select
Columns("V:V").Select
ActiveSheet.Paste
End Sub
I am copying data from sheet(report) to sheet(skurounds)
Give this a try:
Sub tgr()
Dim wb As Workbook
Dim wsReport As Worksheet
Dim wsSKU As Worksheet
Dim dictUnqCompanies As Object
Dim aCompanies As Variant
Dim vCompany As Variant
Dim lDestCol As Long
Set wb = ActiveWorkbook
Set wsReport = wb.Sheets("report")
Set wsSKU = wb.Sheets("skurounds")
Set dictUnqCompanies = CreateObject("Scripting.Dictionary")
lDestCol = wsSKU.Columns("S").Column
'Clear previous results
wsSKU.Range(wsSKU.Cells(1, "S"), wsSKU.Cells(1, wsSKU.Columns.Count)).EntireColumn.Clear
With wsReport.Range("C2", wsReport.Cells(wsReport.Rows.Count, "C").End(xlUp))
If .Row < 2 Then Exit Sub 'No data
If .Rows.Count = 1 Then
'Only 1 row of data
wsSKU.Cells(1, lDestCol).Value = .Value
.Parent.Cells(.Row, "L").Copy wsSKU.Cells(2, lDestCol)
Exit Sub
Else
aCompanies = .Value
End If
End With
For Each vCompany In aCompanies
If Not dictUnqCompanies.exists(vCompany) Then
dictUnqCompanies.Add vCompany, vCompany
With wsReport.Range("C1", wsReport.Cells(wsReport.Rows.Count, "C").End(xlUp))
.AutoFilter 1, vCompany
wsSKU.Cells(1, lDestCol).Value = vCompany
Intersect(.Parent.Columns("L"), .Offset(1).EntireRow).Copy wsSKU.Cells(2, lDestCol)
lDestCol = lDestCol + 1
.AutoFilter
End With
End If
Next vCompany
End Sub

VBA Excel Unfiltering but not unhiding rows

I am writing a code to filter certain data and copy it. After which, I want to unfilter it to its original state. I am using the ActiveSheet.ShowAllData statement but that unhides all the hidden rows as well. Is there a set of code that allows me to unfilter my filtered data but not unhide any rows that were previously hidden?
Thanks for answering
EDIT: This is the code if it helps.
Sub CopyToAmortizing()
Dim tbl As Range
Dim VisibleCells As Integer
Dim lr As Long
Sheets("Template").Select
Columns("A:AZ").EntireColumn.Hidden = False
If Not ActiveSheet.AutoFilter Is Nothing Then Cells.AutoFilter
Range("A5:AB5").Select
Range(Selection, Selection.End(xlDown)).Select
Set tbl = Selection
ActiveSheet.Range("$A$3:$N$9999").AutoFilter Field:=1, Criteria1:= _
"Amortizing Item"
On Error GoTo Point2
VisibleCells = tbl.SpecialCells(xlCellTypeVisible).Rows.Count
If VisibleCells >= 1 Then
Range("A3").Select
Selection.End(xlDown).Activate
lr = ActiveCell.Row
Range("B3", Cells(lr, 12)).Select
Selection.Copy
Sheets("AmortizingItems").Select
Range("A2").Select
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
Rows(2).EntireRow.Delete
Range("A2").Select
Sheets("Template").Select
End If
Point2:
ActiveSheet.ShowAllData
Columns("A:AZ").EntireColumn.Hidden = False
ActiveSheet.Outline.ShowLevels RowLevels:=0, ColumnLevels:=1
Range("A5").Select
End Sub
I use filter:
ActiveSheet.Range("$A$3:$N$9999").AutoFilter Field:=1, Criteria1:= _
"Amortizing Item"
And then: ActiveSheet.ShowAllData
The easy answer is to turn off AutoFilter by using
Sheets("YourSheetName").AutoFilterMode = False
Here is a sample where I add the hidden rows to an array, then re-hide after I am done with them...
Sub SampleHiddenRows()
Set hidrows = New Collection
Set Rng = Range(Cells(5, 4), Cells(13, 5))
For Each cll In Rng
If cll.EntireRow.Hidden = True Then
hidrows.Add cll.Row
End If
Next cll
Rng.AutoFilter field:=1, Criteria1:="one"
Rng.SpecialCells(xlCellTypeVisible).Copy
ActiveSheet.AutoFilterMode = False
For t = 1 To hidrows.Count
Rows(hidrows(t)).Hidden = True
Next t
End Sub

copy filtered value and paste to different worksheet

I have code below help me to copy filtered value and paste to different worksheet.
It always stop at apple... (Apple result looks fine)and pop up Run-time error'1004' Application-defined or object-defined error..
Sub CoWFTR()
'Filter out Apple
Sheet1.Range("A1:ER1").Select
Selection.AutoFilter Field:=11, Criteria1:=Array( _
"ILOVEApple"), Operator:=xlFilterValues
'Copy and Paste to Apple Tab
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Sheets("Apple").Select
ActiveSheet.Paste
Sheet1.Range("A1").Select
Application.CutCopyMode = False
'Clear Filter
On Error Resume Next
Sheet1.ShowAllData
On Error GoTo 0
'Filter out Banana
Sheet1.Range("A1:ER1").Select
Selection.AutoFilter Field:=11, Criteria1:=Array( _
"ILOVEBanana"), Operator:=xlFilterValues
'Copy and Paste to Banana Tab
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Sheets("Banana").Select
ActiveSheet.Paste
Sheet1.Range("A1").Select
Application.CutCopyMode = False
'Clear Filter
On Error Resume Next
Sheet1.ShowAllData
On Error GoTo 0
End Sub
Copy the 2 procedures bellow in the same module, and update FILTER_ITEMS with your criteria:
Option Explicit
Public Sub CoWFTR()
Const FILTER_COL As Long = 11 'K
Const FILTER_ITEMS As String = "ILOVEApple,ILOVEBanana"
Dim wsFrom As Worksheet, wsDest As Worksheet, fi As Variant, i As Long
Set wsFrom = Sheet1 '<--- Update this
fi = Split(FILTER_ITEMS, ",")
Application.ScreenUpdating = False
For i = 0 To UBound(fi)
Set wsDest = CheckNamedSheet(fi(i))
With wsFrom.UsedRange
.AutoFilter Field:=11, Criteria1:="=" & fi(i), Operator:=xlFilterValues
.Copy 'Copy visible data
End With
With wsDest.Cells
.PasteSpecial xlPasteColumnWidths
.PasteSpecial xlPasteAll
Application.CutCopyMode = False
wsDest.Activate
.Cells(1, 1).Select
End With
Next
With wsFrom
.Activate
.Cells(1, 1).Copy
.UsedRange.AutoFilter
End With
Application.ScreenUpdating = True
End Sub
This manages the new sheets
Private Function CheckNamedSheet(ByVal sheetName As String) As Worksheet
Dim ws As Worksheet, result As Boolean, activeWS As Worksheet
Set activeWS = IIf(ActiveSheet.Name = sheetName, Worksheets(1), ActiveSheet)
For Each ws In Worksheets
If ws.Name = sheetName Then
Application.DisplayAlerts = False
ws.Delete 'delete sheet if it already exists
Application.DisplayAlerts = True
Exit For
End If
Next
Set ws = Worksheets.Add(After:=Worksheets(Worksheets.Count)) 'create a new one
ws.Name = sheetName
activeWS.Activate
Set CheckNamedSheet = ws
End Function
For your code, the error you are getting is at this line:
Sheet1.Range("A1").Select
It repeats for Bananas as well, and is triggered by the fact that it tries to select Range("A1") on Sheet1, but the active sheet is Apple (or Banana), so to fix the issues you need to add this line:
Sheet1.Activate
Here is your code, fixed:
Sub CoWFTR()
'Filter out Apple
Sheet1.Range("A1:ER1").Select
Selection.AutoFilter Field:=11, Criteria1:=Array( _
"ILOVEApple"), Operator:=xlFilterValues
'Copy and Paste to Apple Tab
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Sheets("Apple").Select
ActiveSheet.Paste
Sheet1.Activate 'Fix to error 1004
Sheet1.Range("A1").Select
Application.CutCopyMode = False
'Clear Filter
On Error Resume Next
Sheet1.ShowAllData
On Error GoTo 0
'Filter out Banana
Sheet1.Range("A1:ER1").Select
Selection.AutoFilter Field:=11, Criteria1:=Array( _
"ILOVEBanana"), Operator:=xlFilterValues
'Copy and Paste to Banana Tab
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Sheets("Banana").Select
ActiveSheet.Paste
Sheet1.Activate 'Fix to error 1004
Sheet1.Range("A1").Select
Application.CutCopyMode = False
'Clear Filter
On Error Resume Next
Sheet1.ShowAllData
On Error GoTo 0
End Sub
I think it is useful using xlCellTypeVisible. And Use Array.
Sub CoWFTR()
Dim WS As Worksheet, toWs As Worksheet
Dim rngDB As Range, rngTo As Range
Dim vCriteria, vName, i As Integer
Set WS = Sheet1
Set toWs = Sheets("Apple")
Set rngDB = WS.Range("a1").CurrentRegion
vCriteria = Array("ILOVEApple", "ILOVEBanana")
vName = Array("Apple", "Banana")
For i = 0 To UBound(vCriteria)
If WS.FilterMode Then
WS.ShowAllData
End If
Set toWs = Sheets(vName(i))
Set rngTo = toWs.Range("a" & Rows.Count).End(xlUp)(2)
rngDB.AutoFilter Field:=11, Criteria1:=Array( _
vCriteria(i)), Operator:=xlFilterValues
rngDB.SpecialCells(xlCellTypeVisible).Offset(1).Copy rngTo
Next i
If WS.FilterMode Then
WS.ShowAllData
End If
End Sub

SpecialCells(xlCellTypeVisible)

I have 15 columns of data, with rows ranging from 400 - 1000, and I have applied filters, I am keen to only copy visible cells, for column D and J, onto a different sheet, but paste special values through transpose into range D6.
I have used this below method, but it is only copying two visible rows, and not every single row according to the code, like it has done for me in the past for other sheets I have run after amending it. The problem could be I am running three or four process in one macro.
I would be keen to know how I can amend this code so it copies column d and column j visible cells, excluding headers into a different sheet
So where do I stand with the code, it runs and applies the filters, but fails to copy all the rows for this particular part of the macro and secondly, I would be keen to know how to amend it so it only copies the Column D and J as the above excluding headers and only copies visible cells for to paste special values through transpose.
Set rngToCopy = .Offset(1, 0).SpecialCells(xlCellTypeVisible).Copy
Report.Range("D6").PasteSpecial xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=True
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long
Dim rngToCopy As Range, rRange As Range
Set ws = Sheets("Sheet1")
With ws
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rRange = .Range("A1:A" & lRow)
'~~> Remove any filters
.AutoFilterMode = False
With rRange 'Filter, offset(to exclude headers) and copy visible rows
.AutoFilter Field:=1, Criteria1:="<>"
Set rngToCopy = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
'~~> Remove any filters
.AutoFilterMode = False
rngToCopy.Copy
'
'~~> Rest of the Code
'
End With
End Sub
I added thomas code to sub piece to see if the autofilter works and getting error 91
Sub Filter()
Dim Sheetx As Worksheet
Dim rngToCopy As Range, rRange As Range
With Sheetx
Set rRange = .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
With rRange
.AutoFilter Field:=11, Criteria1:="30"
.AutoFilter Field:=4, Criteria1:="1"
.AutoFilter Field:=2, Criteria1:="=*1", _
Operator:=xlAnd
With .SpecialCells(xlCellTypeVisible)
Set rngToCopy = Union(.Offset(0, 3), .Offset(0, 9))
End With
rngToCopy.Copy
End With
End With
End Sub
We can use Union and Range.Offset to join the cells together define the range.
MSDN: Application.Union Method (Excel)
Returns the union of two or more ranges.
Sub Sample()
Dim lRow As Long
Dim rngToCopy As Range, rRange As Range
With Sheets("Sheet1")
With .Range("A1").CurrentRegion
.AutoFilter Field:=11, Criteria1:="=30"
.AutoFilter Field:=4, Criteria1:="=1"
.AutoFilter Field:=2, Criteria1:="=1", _
Operator:=xlAnd
On Error Resume Next
Set rngToCopy = .SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rngToCopy Is Nothing Then
MsgBox "SpecialCells: No Data", vbInformation, "Action Cancelled"
Exit Sub
End If
Set rngToCopy = Intersect(rngToCopy, .Range("B:B,H:H"))
If rngToCopy Is Nothing Then
MsgBox "Intersect: No Data", vbInformation, "Action Cancelled"
Exit Sub
End If
End With
End With
rngToCopy.Copy
Sheets("Sheet2").Range("C6").PasteSpecial xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=True
End Sub

Run time error in excel vba. last used range rows selects row 65536 instead of actual last used range

I am trying to select columns E and K from sheet Input, process in Working sheet and paste in the Output sheet after the last used row. I have stored the last used row number in x and paste the values in x+1 cell. However excel selects last row of the sheet (x as 65536) and gives run time error 4004. Can someone please help me in assisting the code.
Dim x As Long, y As String
Sheets("Input").Activate
Range("E:E,K:K").Select
Range("K1").Activate
Selection.Copy
Sheets("Working").Select
Cells(1, 1).Select
ActiveSheet.Paste
Cells.Select
Application.CutCopyMode = False
Selection.AutoFilter
Range("B5").Select
ActiveSheet.Range("$A$1:$H$30").AutoFilter Field:=1, Criteria1:="="
Cells.Select
Selection.Delete Shift:=xlUp
Columns("B:B").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("B2").Select
ActiveCell.FormulaR1C1 = _
"=IF(RC[-1]="""","""",VLOOKUP(RC[-1],Repository!C[-1]:C[1],3,0))"
Range("B2").Select
Selection.Copy
Range("B3:B30").Select
ActiveSheet.Paste
Cells.Select
Application.CutCopyMode = False
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("B2:C2").Select
Range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Output").Select
Range("A1").Select
x = Worksheets("Output").UsedRange.Rows.Count
y = "a" & Trim(x + 1)
ActiveSheet("Output").Range(y).Select
ActiveSheet.Paste
Your UsedRange is still thinking that the last row is 65536. Add this subroutine, then call it right before you set x.
Sub CorrectUsedRange()
Dim values
Dim usedRangeAddress As String
Dim r As Range
'Get UsedRange Address prior to deleting Range
usedRangeAddress = ActiveSheet.UsedRange.Address
'Store values of cells to array.
values = ActiveSheet.UsedRange
'Delete all cells in the sheet
ActiveSheet.Cells.Delete
'Restore values to their initial locations
Range(usedRangeAddress) = values
End Sub
Near the bottom of your code replace:
Sheets("Output").Select
with:
Sheets("Output").Select
ActiveSheet.UsedRange
this should "re-set" UsedRange
Sometimes the Used Range gets generically large and won't reset on it's own. When this happens, the only way that I've found to force it to reset itself correctly is to Save the Workbook that the subject Worksheet is in. This works for me, on Excel 2010 anyway. Since you're using .Select and Active<obj> (which I don't recommend), it would simply be this:
ActiveWorkbook.Save
I would use a Find loop to populate an array and then output the array when the macro has completed. There is no need for a "Working" sheet. This also uses Cells(Rows.Count, "A").End(xlUp) in order to find the last populated row instead of UsedRange.Rows.Count which can be unreliable.
Sub tgr()
Dim rngFound As Range
Dim rngLookup As Range
Dim arrResults() As Variant
Dim ResultIndex As Long
Dim strFirst As String
With Sheets("Input").Columns("E")
Set rngFound = .Find("*", .Cells(.Cells.Count), xlValues, xlWhole)
If Not rngFound Is Nothing Then
strFirst = rngFound.Address
ReDim arrResults(1 To WorksheetFunction.CountA(.Cells), 1 To 2)
Do
If rngFound.Row > 1 Then
ResultIndex = ResultIndex + 1
On Error Resume Next 'Just in case the VLookup can't find the value on the 'Repository' sheet
arrResults(ResultIndex, 1) = Evaluate("VLOOKUP(""" & rngFound.Value & """,Repository!A:C,3,FALSE)")
arrResults(ResultIndex, 2) = .Parent.Cells(rngFound.Row, "K").Value
On Error GoTo 0 'Remove the On Error Resume Next condition
End If
Set rngFound = .Find("*", rngFound, xlValues, xlWhole)
Loop While rngFound.Address <> strFirst
End If
End With
If ResultIndex > 0 Then Sheets("Output").Cells(Rows.Count, "A").End(xlUp).Offset(1).Resize(ResultIndex, UBound(arrResults, 2)).Value = arrResults
Set rngFound = Nothing
Erase arrResults
End Sub
instead of used range check how many rows already are filled with this code:
X = WorksheetFunction.CountA(Columns(1))
Of course this only works ok if you have no rows that are empty in Column A, as those rows would be ignored!