SpecialCells(xlCellTypeVisible) - vba

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

Related

Trying to use a 3rd criteria for autofilter, getting compile error?

Is the syntax limit really just two? So I have to either use an array or just repeat the block of code? The error is at Criteria3:= in my .AutoFilter Field - Compile error: Named argument not found. I'm just surprised that it's limited to two. What is the reason behind this?
Sub CleanData()
'B b b b boilerplate!
Dim sht As Worksheet, lastrow As Long, myrange As Range
'Set references up-front
Set sht = ThisWorkbook.Worksheets("MySheet")
'Identify the last row and use that info to set up the Range
With sht
lastrow = .Cells(sht.Rows.Count, "A").End(xlUp).Row
Set myrange = .Range("A2:AS" & lastrow)
End With
Application.DisplayAlerts = False
With myrange
'Apply the Autofilter method to the first column of
.AutoFilter Field:=26, _
Criteria1:="Operator Error", _
Operator:=xlOr, _
Criteria2:="Duplicate", _
Operator:=xlOr, _
Criteria3:="Training/Test"
'ERROR HERE
'Delete the visible rows while keeping the header
On Error Resume Next
.Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Rows.Delete
On Error GoTo 0
End With
Application.DisplayAlerts = True
'Turn off the AutoFilter
With sht
.AutoFilterMode = False
If .FilterMode = True Then
.ShowAllData
End If
End With
End Sub
Criteria3 is not a valid parameter., but to get around this, you can use an array:
(Filtering on multiple states)
Criteria1:=Array("IL", "IN", "MI", "OH", "WV"), Operator:=xlFilterValues

VBA - PasteSpecial not working after using SpecialCells.Copy

To sum up, I try to copy some filtered data from a workbook A to a workbook B keeping the formatting of the workbook B.
Here is the relevant part of my code:
With originSheet
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
.SpecialCells(xlCellTypeVisible).Copy
End With
End With
destinationSheet.Range("B4").PasteSpecial xlPasteValues
The paste special is not working and this is the formatting of the workbook A that is used.
Solved:
The problem was that you can't use PasteSpecial in a discontinuous range.
So I went with the solution of Siddharth Rout to go through all the areas of the filtered range:
With originSheet
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
Set filteredRange = .Offset(1, 0).SpecialCells(xlCellTypeVisible)
'~~> Loop through each area
For Each area In filteredRange.Areas
With destinationSheet
'~~> Find Next available row
lRow = .Range("B" & .Rows.Count).End(xlUp).Row + 1
area.Copy
destinationSheet.Range("B" & lRow).PasteSpecial xlPasteValues
End With
Next area
End With
End With
What #Jeeped has mentioned is very true that you cannot used Paste Special on a filtered range if they are Non Contiguous. However there is a way to achieve what you want :)
You have to loop through each area of the filtered range and then use Paste Special as shown below
Sub Sample()
Dim ws As Worksheet
Dim lastRowOriginSheet As Long
Dim filteredRange As Range, a As Range
Dim projectNumber As Long
'~~> I have set these for testing. Change as applicable
projectNumber = 1
Set ws = Sheet1
Set destinationSheet = Sheet2
lastRowOriginSheet = 16
With ws
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
Set filteredRange = .Offset(1, 0).SpecialCells(xlCellTypeVisible)
'~~> Loop through each area
For Each a In filteredRange.Areas
With destinationSheet
'~~> Find Next available row
lRow = .Range("B" & .Rows.Count).End(xlUp).Row + 1
a.Copy
destinationSheet.Range("B" & lRow).PasteSpecial xlPasteValues
End With
Next a
End With
End With
End Sub
In Action
PasteSpecial does not work on a discontiguous range. If you have one hidden row in among visible rows then you have a discontiguous range. However, due to the nature of a discontiguous range, a straight copy and paste will paste formats and the values from formulas; i.e. it cannot determine how to shift the cell ranges in formulas so it just pastes values.
With originSheet
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
'you should probably check to ensure you have visible cells before trying to copy them
.SpecialCells(xlCellTypeVisible).Copy destination:=destinationSheet.Range("B4")
End With
End With
Try this. Instead of doing PasteSpecial, since you just need values, you can set the ranges equal to eachother.
Dim copyRng As Range
With originSheet
.AutoFilterMode = False
With .Range("A7:AA" & lastRowOriginSheet)
.AutoFilter Field:=2, Criteria1:=projectNumber
Set copyRng = .SpecialCells(xlCellTypeVisible)
End With
End With
' destinationSheet.Range("B4").Value = copyRng.Value
With destinationSheet
.Range(.Cells(4, 2), .Cells(4 + copyRng.Rows.Count - 1, 2 + copyRng.Columns.Count - 1)).Value = copyRng.Value
End With
(this is assuming your worksheet and lastRow and projectNumber are all declared properly and working).
Edited because if you just do Range("B4").Value = Range("A1:Z100").Value, it's only going to put the first value in your copied range in the cell. You need to expand the destination range to be the size of the copy range.

Excel VBA loop until

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

Deleting rows with specific words with VBA

currently my excel data consists of specific words and #N/A, words are like "build One" "proj ex".. I have prepared a code in which it only deletes one condition but I want it for many words. below is my code. Any help is welcome. Thanks.
Sub del()
Dim rng1 As Range
Set rng1 = Range([B2], Cells(Rows.Count, "B").End(xlUp))
ActiveSheet.AutoFilterMode = False
With rng1
.AutoFilter Field:=1, Criteria:=("#N/A")
.Delete xlUp
End With
End Sub
Use a variant array as a constructor for your word list.
Sub del()
Dim rng1 As Range, vDELs As Variant
vDELs = Array("#N/A", "proj ex", "build One")
Set rng1 = Range([B2], Cells(Rows.Count, "B").End(xlUp))
ActiveSheet.AutoFilterMode = False
With rng1
.AutoFilter Field:=1, Criteria1:=(vDELs), Operator:=xlFilterValues
With .Offset(1, 0)
If CBool(Application.Subtotal(103, .Cells)) Then _
.EntireRow.Delete
End With
.AutoFilter
End With
End Sub
Good catch on bracketing the array in Criteria1:=(vDELs). That is important. Also a good idea to check if you have rows to delete before committing to the operation.
you could try something like:
sFormula = "=IF(OR(ISERROR(B:B),B:B=""proj ex"", B:B=""build One""),NA(),"""")"
Set rng1 = Range("A2:A" & Cells(Rows.Count, "B").End(xlUp).Row)
rng1.Formula = sFormula
' Now use SpecialCells to remove the rows:
rng1.SpecialCells(xlCellTypeFormulas, xlErrors).EntireRow.Delete shift:=xlUp
more on this type of technique at SO: How to delete multiple rows in Excel without a loop and Ron de Bruin Excel VBA Top Banana: Special Cells limit bug

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!