Exclude rows with formulas from range in macro - vba

I made a Data Entry Form that ads or updates rows in a datasheet. With this http://www.contextures.com/exceldataentryupdateform.html as the base. The form has 128 rows and 5 of those are vlookup formulas (row 12, 19, 30, 34, 36) that should be excluded when using the view record navigation buttons. Otherwise the formulas get deleted and replaced by a value, if you use the nav buttons.
But I really have no clue how to do this. I'm really new to VBA. This is my first project so all help will be greatly appreciated.
Sub ViewLogDown()
Dim historyWks As Worksheet
Dim inputWks As Worksheet
Dim rngA As Range
Dim lRec As Long
Dim lRecRow As Long
Dim lLastRec As Long
Dim lastRow As Long
Application.EnableEvents = False
Set inputWks = Worksheets("Input")
Set historyWks = Worksheets("Werknemers")
Set rngA = ActiveCell
With historyWks
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row - 1
lLastRec = lastRow - 1
End With
With inputWks
lRec = .Range("CurrRec").Value
If lRec < lLastRec Then
.Range("CurrRec").Value = lRec + 1
lRec = .Range("CurrRec").Value
lRecRow = lRec + 1
historyWks.Range(historyWks.Cells(lRecRow, 3), historyWks.Cells(lRecRow, 128)).Copy
.Range("D5").PasteSpecial Paste:=xlPasteValues, Transpose:=True
inputWks.Range("OrderSel").Value = .Range("D5").Value
rngA.Select
End If
End With
Application.EnableEvents = True
End Sub

If you want to copy and paste and exclude formula-based cells then you can use the SpecialCells method of the Range object. `xlCellTypeConstants' will filter out cells without a formula and blank cells.
E.g. with your code:
Dim rngSource As Range
Dim rngFilter As Range
Set rngSource = historyWks.Range(historyWks.Cells(lRecRow, 3), historyWks.Cells(lRecRow, 128))
Set rngFilter = rngSource.SpecialCells(xlCellTypeConstants)
Note once pasted the Range will be smaller than the original because the cells with formulas are discounted.
You can Union different calls SpecialCells together. So to include blanks you could use:
Dim rngSource As Range
Dim rngFilter As Range
Set rngSource = historyWks.Range(historyWks.Cells(lRecRow, 3), historyWks.Cells(lRecRow, 128))
Set rngFilter = Union( _
rngSource.SpecialCells(xlCellTypeConstants), _
rngSource.SpecialCells(xlCellTypeBlanks) _
)
Sample code for minimal example of use of SpecialCells:
Option Explicit
Sub TestRangeCopyExcludingFormulas()
Dim ws As Worksheet
Dim rngToCopy As Range
Dim rngToCopyExcludingFormulas As Range
Dim rngToPaste As Range
Dim rngCell As Range
' set the worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
' set the range to copy excluding formulas
Set rngToCopy = ws.Range("B3:B13")
' copy just the constants
' Set rngToCopyExcludingFormulas = rngToCopy.SpecialCells(xlCellTypeConstants)
' copy constants and blanks
Set rngToCopyExcludingFormulas = Union( _
rngToCopy.SpecialCells(xlCellTypeConstants), _
rngToCopy.SpecialCells(xlCellTypeBlanks))
' set the range to paste to
Set rngToPaste = ws.Range("E3")
' do the copy and paste
rngToCopyExcludingFormulas.Copy
rngToPaste.PasteSpecial Paste:=xlPasteValues
' use transpose etc
' rngToPaste.PasteSpecial Paste:=xlPasteValues, Transpose:=True
' remove the dancing ants
Application.CutCopyMode = False
End Sub
See screenshot:

Related

VBA - Find lastColumn and Check if First Row Is Empty

I am not too sure how I should solve this issue, but there are two methods that make sense to me..
Some of the sheets in my workbook do not have headers, so I use the below code to insert a blank row and assign a header to column A - I know column A will always be employee number.
Sub insertRow()
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Dim wkbk1 As Workbook
Set wkbk1 = Workbooks("testWorkbook.xlsm")
'Set sheets to be used in each workbook
Set ws1 = wkbk1.Sheets("mySheet")
Set ws2 = wkbk1.Sheets("hisSheet")
Set ws3 = wkbk1.Sheets("herSheet")
wkbk1.Activate
ws1.Range("A1").EntireRow.Insert
ws1.Range("A1").Value = "Employee Number"
ws2.Range("A1").EntireRow.Insert
ws2.Range("A1").Value = "Employee Number"
ws3.Range("A1").EntireRow.Insert
ws3.Range("A1").Value = "Employee Number"
End Sub
The below code deletes columns based on the header name.
Sub ManipulateSheets()
Dim a As Long, w As Long
Dim keepCols As Variant
Dim wkbk1 As Workbook
Set wkbk1 = Workbooks("testWorkbook.xlsm")
keepCols = Array("Employee Number", "Status")
wkbk1.Activate
With wkbk1
For w = 1 To .Worksheets.count
With Worksheets(w)
For a = .Columns.count To 1 Step -1
If UBound(Filter(keepCols, .Cells(1, a), True, vbTextCompare)) < 0 Then _
.Columns(a).EntireColumn.Delete
Next a
End With
Next w
End With
End Sub
The issue is this:
The 3 sheets that I insert a row in and set the column header for column A to Employee Number, still has empty headers for the remainder of the row.. So when I run the code above to delete the columns, nothing happens on these 3 sheets as there is no data to compare to in the header.
So the two options I thought would work are:
Find the lastColumn and insert text into the cells between column A and the lastColumn
Find the lastColumn and include a criteria in the if statement that looks for blank cells as well as non-matching headers
I got the code to find the lastColumn here:
Excel VBA- Finding the last column with data
Sub findColumn()
Dim rLastCell As Range
Dim i As Long
Dim MyVar As Variant
Dim ws1 As Worksheet
Dim wkbk1 As Workbook
i = 2
Set wkbk1 = Workbooks("testWorkbook.xlsm")
Set ws2 = wkbk1.Sheets("ws1")
Set rLastCell = ws2.Cells.Find(What:="*", After:=ws2.Cells(1, 1), LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
End Sub
I'm thinking of a Do While Loop along the lines of the below:
Do While (MyVar1 >= 2 And MyVar1 < rLastCell.Column)
Loop
You could change the condition to
IsError(Application.Match(rng.Value, keepCols, 0))
That picks up all non-matching items.
Note:
For Each rng In Intersect(.Rows(1), .UsedRange)
The above is going to loop through the used range of row 1 in the selected sheet.
Looping multiple sheets might be something like:
Option Explicit
Sub test()
Dim keepCols()
keepCols = Array("Employee Number", "Status")
Dim unionRng As Range, rng As Range, ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
With ws
If Application.WorksheetFunction.CountA(.Rows(1)) > 0 Then
For Each rng In Intersect(.Rows(1), .UsedRange)
If IsError(Application.Match(rng.Value, keepCols, 0)) Then
If Not unionRng Is Nothing Then
Set unionRng = Union(unionRng, rng)
Else
Set unionRng = rng
End If
End If
Next rng
If Not unionRng Is Nothing Then
Debug.Print unionRng.EntireColumn.Address 'unionRng.EntireColumn.Delete '. ''<== Swop when ready to delete
Set unionRng = Nothing
End If
End If
End With
Next ws
End Sub

Selecting the last used cell in column A and then extend it to column H

Hi there I am trying to select a range "A2:H2" down to the last filled cell based on column A (so in this case it should select "A2:H59"). The range is not fixed so it cannot be defined with exact numbers. I have the following code, but it selects everything down to the 402nd row even though there is no data beyond "A59" in the sheet. Any idea what is going on? Thanks for the help!
Global ssaw As Worksheet
Global trckr As Worksheet
Sub DataF()
Dim myRange As Range
Dim myCell As Range
Set ssaw = Sheets("SSAW_DATA")
Set trckr = Sheets("SQL_DATA_FEED")
Set myRange = trckr.Range("A2:H2").end(xlDown)
With myRange
.SpecialCells(xlCellTypeBlanks).Interior.Color = RGB(255, 102, 102)
.SpecialCells(xlCellTypeBlanks).Value = "#missing#"
End With
End Sub
If we assume your last used cell in column A is A59 then …
… This
Set myRange = trckr.Range("A2", trckr.Range("A2").End(xlDown))
will select A2:A59 and this
.Resize(ColumnSize:=8)
will resize it to make it 8 columns width that is A2:H59.
So together we get:
Set myRange = trckr.Range("A2", trckr.Range("A2").End(xlDown)).Resize(ColumnSize:=8)
Use this
trckr.Range("A" & trckr.Rows.Count).End(xlUp)
alternatively to find the last used cell in column A if there can be empty cells in between:
Set myRange = trckr.Range("A2", trckr.Range("A" & trckr.Rows.Count).End(xlUp)).Resize(ColumnSize:=8)
exploit the fact that Range(cell1, cell2) is equivalent to Range(cell2, cell1)
Set myRange = trckr.Range("H2", trckr.Range("A2").End(xlDown))
while if you want to select a range from A2:H2 down to column A last not empty cell (i.e. included empty cells along column A in between the first and last not empty ones):
Set myRange = trckr.Range("H2", trckr.Cells(trckr.Rows.Count, 1).End(xlUp))
I would suggest to use the following code
Option Explicit
Function LastRowInColumn(colName As String)
Dim lastRow As Long
With ActiveSheet
lastRow = .Cells(.Rows.Count, colName).End(xlUp).Row
End With
LastRowInColumn = lastRow
End Function
Sub SelectRg()
Dim rg As Range
Dim wks As Worksheet
Dim lastRow As Long
lastRow = LastRowInColumn("A")
Debug.Print lastRow
If lastRow = 1 Then
' do nothing
Else
Set wks = ActiveSheet
With wks
Set rg = Range(.Cells(2, 1), .Cells(lastRow, "H"))
rg.Select
End With
End If
End Sub
The code determins the last filled row in column A and select based on this information everything to column H
EDIT Improved function
Function LastRowInColumn(ByVal wks As Worksheet, ByVal colName As String) As Long
With wks
LastRowInColumn = .Cells(.Rows.Count, colName).End(xlUp).Row
End With
End Function
EDIT2 And if one would not like to use an extra function you could do it like that
Sub SetRg()
Dim rg As Range
Dim wks As Worksheet
Dim lastRow As Long
Set wks = ActiveSheet
With wks
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
'lastRow = LastRowInColumn(wks, "A")
If lastRow > 1 Then
Set rg = Range(.Cells(2, 1), .Cells(lastRow, "H"))
End If
End With
End Sub

Copy Union of multiple columns from one sheet to another

I wrote a code to copy Column D, H, M and paste it on a brand new sheet starting from A-C. I first find the last row , after that I Union the 3 column range together then select the sheet and paste it.
For some reason I don't understand why it does not work. I have never used Union range before so not sure if that is the problem, or if it is something like my for loop. Help would be appreciated.
Dim ws As Worksheet
Dim lastRow As Integer
'for loop variables
Dim transCounter As Integer
Dim range1 As Range
Dim range2 As Range
Dim range3 As Range
Dim multipleRange As Range
Dim lastRow1 As Integer
Dim ittercell As Integer
Set ws = ActiveSheet
For transCounter = 1 To 10
r.AutoFilter Field:=6, Criteria1:=transCounter.Value, Operator:=xlFilterValues
With Application.ActiveSheet
lastRow1 = .Cells(.Rows.Count, "AE").End(xlUp).Row
End With
Set range1 = Sheets("Sheet1").Range("D6:D" & lastRow1).SpecialCells(xlCellTypeVisible)
Set range2 = Sheets("Sheet1").Range("H6:I" & lastRow1).SpecialCells(xlCellTypeVisible)
Set range3 = Sheets("Sheet1").Range("M6:M" & lastRow1).SpecialCells(xlCellTypeVisible)
Set multipleRange = Union(range1, range2, range3)
multipleRange.Copy
Sheets("O1 Filteration").Select
'Range("A3").Select
'Range("A3").PasteSpecial xlPasteValues
ittercell = 1
Cells(3, ittercell).PasteSpecial xlPasteValues
ittercell = ittercell + 6
Next transCounter
There's a couple of issues with your code that might be causing the fault:
r is not defined in your code
use of transCounter.Value instead of just CStr(transCounter) (see #QHarr comment)
iterCell reset every iteration of the loop (see #QHarr comment)
Combination of ActiveSheet, unqualified Cells(... and manual Select on sheets makes the Range qualifications ambiguous
However, I do think the main logic of using Union, then Copy, then PasteSpecial is OK and just some tweaking is required.
Here is some working code where you update the Worksheet and Range references with your own. Please follow the comments.
Option Explicit
Sub CopyUnionColumns()
Dim wsSource As Worksheet '<-- Sheet1 in your code
Dim wsTarget As Worksheet '<-- O1 Filteration in your code
Dim rngFilter As Range '<-- main data range on Sheet1
Dim rngSource As Range '<-- to hold Union'd data after filtering
Dim rngTarget As Range '<-- range in O1 Filteration to paste code to
Dim lngLastRow As Long '<-- last row of main data
Dim lngCounter As Long '<-- loop variable
Dim lngPasteOffsetCol As Long '<-- offset column for pasting in the loop
' set references to source and target worksheets
Set wsSource = ThisWorkbook.Worksheets("Sheet2") '<-- update for your workbook
Set wsTarget = ThisWorkbook.Worksheets("Sheet3") '<-- update for your workbook
' set reference to data for filtering in source worksheet
lngLastRow = wsSource.Cells(wsSource.Rows.Count, 6).End(xlUp).Row
Set rngFilter = wsSource.Range("A1:F" & lngLastRow)
' initialise offset column
lngPasteOffsetCol = 0
' iterate rows
For lngCounter = 1 To 10
' filter data the data per the counter
rngFilter.AutoFilter Field:=6, Criteria1:=CStr(lngCounter), Operator:=xlFilterValues
' set source range as union of columnar data per last row
Set rngSource = Application.Union( _
wsSource.Range("A1:A" & lngLastRow).SpecialCells(xlCellTypeVisible), _
wsSource.Range("C1:C" & lngLastRow).SpecialCells(xlCellTypeVisible), _
wsSource.Range("E1:E" & lngLastRow).SpecialCells(xlCellTypeVisible))
' set target range on target sheet top left cell and offset column
Set rngTarget = wsTarget.Range("A1").Offset(0, lngPasteOffsetCol)
' copy source cells
rngSource.Copy
' paste to target
rngTarget.PasteSpecial Paste:=xlPasteAll
' increment offset
lngPasteOffsetCol = lngPasteOffsetCol + 6
Next lngCounter
' cancel cut copy mode
Application.CutCopyMode = False
' cancel autofilter
wsSource.AutoFilterMode = False
End Sub

VBA index/match blank cell

I have a list of accounts & codes, and would like to populate a column with results from a different sheet using index match. I can get it to work using the formula: =index(rngB,match(BCode,rngM,0),55)
but can't translate it to vba. I have tried:
sub compare()
Dim BudgetResult As Long
Dim var1 As Long
Dim rngB, rngM As Range
Dim CompSH, ActSH, BudSH As Worksheet
Dim BCode As Variant
Set CompSH = Sheets.Add(After:=Sheets(Sheets.Count))
Set ActSH = Sheets(2)
Set BudSH = Sheets(3)
Set rngB = BudSH.Range("B11:BF50")
Set rngM = BudSH.Range("B:B")
Set BCode = CompSH.Range("A2")
BudSH.Select
Range("B10:E76").Select
Selection.Copy
CompSH.Select
ActiveSheet.Paste
Range("F1").Select
ActiveCell.FormulaR1C1 = "Budget"
Range("F2").Select
With Application.WorksheetFunction
var1 = .Match(BCode, rngM, 0)
BudgetResult = .Index(rngB, var1, 55)
End With
I get a blank cell. no result in the sheet.
Also, I don't know how to continue it down. Can anyone help?
you may be after something like follows
Option Explicit
Sub compare()
Dim rngB As Range, rngM As Range, cell As Range
Dim CompSH As Worksheet, ActSH As Worksheet, BudSH As Worksheet
Dim AW As WorksheetFunction: Set AW = Application.WorksheetFunction
Set CompSH = Sheets.Add(After:=Sheets(Sheets.count))
Set ActSH = Sheets("ActSH") 'Sheets(2)
Set BudSH = Sheets("BudSH") 'Sheets(3)
With BudSH
Set rngB = .Range("B11:BF50") '<--| warning: your "index" range has 40 rows
Set rngM = .Range("B:B")
.Range("F1").Value = "Budget"
.Range("B10:E76").Copy CompSH.Range("A1") '<--| warning: your "copied" range has 67 rows
End With
With CompSH
For Each cell In .Range("A2", .Cells(.Rows.count, 1).End(xlUp))
cell.Offset(, 5).Value = AW.Index(rngB, AW.Match(cell, rngM, 0), 55) '<--| this will error when trying to access from 'rngB' range 41th rows on
Next
End With
End Sub
where you only have to adjust the range sizes in the statements marked with <--| Warning...

VBA VLOOKUP Copy/Paste

I have a VBA code that copies and pastes pages and copies everything properly except any cell that has a vlookup. I don't know what the problem is or if there may be some lag time. Any ideas would be helpful.
Dim thisSheet As Worksheet
Set thisSheet = Application.ActiveSheet
Dim templateSheet As Worksheet
Set templateSheet = Worksheets("Template ")
Dim baseSheet As Worksheet
Set baseSheet = Worksheets("Base")
Dim wrkBook As Workbook
Set wrkBook = ActiveSheet.Parent
Dim wrkSheet As Worksheet
Dim sNewSheetName As String
Dim nRow As Integer
Application.ScreenUpdating = False
Dim rRunRange As Range
Set rRunRange = Range("Cohorts_to_Run")
Dim n As Integer
For nRow = 1 To rRunRange.Rows.Count
sNewSheetName = rRunRange.Cells(nRow, 2).Value
Set wrkSheet = Worksheets.Add(, Worksheets(Worksheets.Count))
wrkSheet.Name = sNewSheetName
'Set parameters to pickup data....
Dim zZoom As Double
zZoom = ActiveWindow.Zoom
templateSheet.Activate
templateSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value).Copy
wrkSheet.Activate
wrkSheet.PasteSpecial xlPasteColumnWidths
wrkSheet.Paste
ActiveWindow.Zoom = baseSheet.Range("ZOOM_LEVEL").Value
wrkSheet.Range("A1").RowHeight = 10
Application.CutCopyMode = False
wrkSheet.Range("B2").Value = Range("Cohorts_to_Run").Cells(nRow, 1).Value
Dim sTitleRangeName As String
sTitleRangeName = sNewSheetName & "_Title"
wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value).Select
Selection.Copy
wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value).PasteSpecial
xlPasteValuesAndNumberFormats
Next nRow
I assume your confusion is that last line which pastes only values and removes any formulas.
wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value).PasteSpecial xlPasteValuesAndNumberFormats
Why not just paste? xlPasteValuesAndNumberFormats will remove your formulas everytime. That's the point.
Dim copyRange as Range, pasteRange as Range
Set copyRange = wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value)
Set pasteRange = wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value)
copyRange.Copy pasteRange
You're wrkSheet.Range(baseSheet.Range("TEMPLATE_COPY_RANGE").Value) syntax is confusing though. Is there the name of another range in your named range TEMPLATE_COPY_RANGE?