Converting Excel Formula to VBA and use Array - vba

I need to convert the following excel formula to VBA code:
=C10+H:H
UPDATE:
I want to take the values in column H of Worksheet1 and add them to the value in cell C10. I then want to compare that new value to a range of values in column C in Worksheet 2. I have been using the entire column because the rows that use the data in that column fluctuate.
I want to be able to look at the data a decade at a time. So at 10 years I am using Worksheet1!H20:H30. Then at 20 years I would use Worksheet1!H20:H40 and so on.
I could reasonably for VBA purposes limit the range from the entire column to Worksheet1!H20:H1000 (maybe even less, not sure just yet). The data in column H is what I need to put in an array and then need to be able to add to that data (will probably need to store result in second array) and then compare the result to an additional grouping of data on a separate worksheet (i.e. Worksheet2!C:C).
I need to calculate age based on annual incremental increases. I have my starting age stored in C10. I have the annual increments stored in column H. The way it works in excel is if I have this in cell B22 it add C10 to H22.
This formula is wrapped in a long and daunting if statement. It compares the calculated age to an age range on a different worksheet in lets say column C (i.e. Worksheet2!C:C = C10 + Worksheet1!H:H).
I need to be able to do that in VBA.
I tried in VBA
ElseIf Worsheets("Sheet2").Range("C:C") = C10 + Worsheets("Sheet1").Range("H:H")
It throw a match error.
UPDATE: FOR LOOP IN FUNCTION I TRIED TO USE
Function arrCalc_30yrMax_age_annual_incr_stnd_mbr() As Variant
'SETS THE VARIABLES
Dim wb As Workbook
Dim ws_Calculator_TL_30yrMaxTerm As Worksheet
Dim ws_LVRates As Worksheet
Dim ws_TLRates As Worksheet
Dim ws_Misc As Worksheet
Dim rngCalc_30yrMax_age_mbr As Range
Dim rngCalc_30yrMax_age_sp As Range
Dim rngCalc_30yrMax_age_annual_incr As Range
Dim arrCalc_30yrMax_age_annual_incr As Variant
Dim arrAge_Annual_Inc As Variant
Dim Row As Long
Dim Column As Long
'SETS RANGE FOR rngCalc_30yrMax_age_annual_incr RANGE
Set wb = ThisWorkbook
Set ws_Calculator_TL_30yrMaxTerm = Worksheets("Calculator_TL_30yrMaxTerm")
Set ws_LVRates = wb.Worksheets("LVRates")
Set ws_TLRates = wb.Worksheets("TLRates")
Set ws_Misc = wb.Worksheets("Misc")
Set rngCalc_30yrMax_age_annual_incr = ws_Calculator_TL_30yrMaxTerm.Range("Calc_30yrMax_age_annual_incr")
'SETS ARRAY EQUAL TO rngCalc_30yrMax_age_annual_incr
arrCalc_30yrMax_age_annual_incr = rngCalc_30yrMax_age_annual_incr
'LOOP THROUGH THE ARRAY OF WORKSHEET VALUES
For Row = 1 To UBound(arrCalc_30yrMax_age_annual_incr, 1) 'First array dimension is rows.
For Column = 1 To UBound(arrCalc_30yrMax_age_annual_incr, 1) 'Secong array dimension is columns.
arrAge_Annual_Inc = arrCalc_30yrMax_age_annual_incr + rngCalc_30yrMax_age_mbr
Next Column
Next Row
End Function

Absent data and desired results, I remain uncertain as to just what you want. Perhaps this approach will help, if my assumptions are correct. If not, I'll delete it.
I think what you are trying to do (based on your ELSEIF statement above)
examine each value in Column C
find the value in Column H that is equal to that value +10
if you find such a value, do something.
if your comparison is more complicated (eg finding an inexact match, and the values in column H are sorted, a different approach should be used)
Here is one way of doing it. (and there are a number of others, in addition to looping through all the data -- exactly which is best (fastest) depends on the circumstances.
Option Explicit
'Worsheets("Sheet2").Range("C:C") = C10 + Worsheets("Sheet1").Range("H:H")
Sub Marine()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim r1 As Range, r2 As Range
Dim vToFind 'declare as a type of value ?date ?long ?double
Dim c1 As Range, c2 As Range
Set ws1 = Worksheets("sheet1")
Set ws2 = Worksheets("sheet2")
'set ranges to end at the last used cell in the columns
With ws1
Set r1 = .Range(.Cells(1, "C"), .Cells(.Rows.Count, "C").End(xlUp))
End With
With ws2
Set r2 = .Range(.Cells(1, "H"), .Cells(.Rows.Count, "H").End(xlUp))
End With
For Each c1 In r1
vToFind = c1 - Range("c10")
Set c2 = r2.Find(what:=vToFind, after:=r2(1), _
LookIn:=xlValues, searchdirection:=xlNext)
If Not c2 Is Nothing Then
'do your thing
Stop
Else
MsgBox vToFind & " not found in target"
End If
Next c1
End Sub

Related

Vba clear contents of a certain range or cell

How can I clear the column and row starting with a reference cell?
I used
For x = 1 To 20
Sheets(1).Columns(x).ClearContents
Next x
But I want to be able to clear the contents of all rows and columns starting with row A6 as a reference point. I cant use range since the data is dynamic and changes upon insertion of data. The data came from a csv
Since your question states "I want to be able to clear the contents of all rows and columns starting with row A6 as a reference point." Then here is a one liner using SpecialCells(xlLastCell)
ActiveSheet.Range("A6", ActiveCell.SpecialCells(xlLastCell)).Clear
If your range starts at A6 and is continuous with no blanks in Row 6 and no blanks in Column A, you could set the range like this:
'Create variables
Dim wb as Workbook
Dim ws as Worksheet
Dim rng as Range
'Initialize variables
set wb = ActiveWorkbook
set ws = ActiveSheet
lastrow = ws.Range("A6").End(xlDown).Row
lastcol = ws.Range("A6").End(xlToRight).Column
'Set the range
set rng = ws.Range(Cells(6,1),Cells(lastrow,lastcol))
'Clear contents
rng.ClearContents
This uses Range.Endproperty of the Range object. It's basically like clicking on A6 and hitting ctrl+right on your keyboard and then returning the range.
Note: If there are gaps in the range you won't get the correct result.

Looping through filter criteria for a column and performing analysis on visible data to copy to new sheet - VBA Macro

Effectively what I am trying to accomplish is to filter a database (all in Sheet 1) by names, which are given in column A, and then perform various data analysis on the now filtered visible data, and then copy that data to a new sheet in a given cell.
For example, filter the data in Sheet 1 by the name 'Smith' in column A and then let's say sum all of the visible data in column B and print that to cell C3 on Sheet 2. The more advanced data analysis I am sure I can tackle on my own, just want to get rolling here and I am definitely new to VBA macro coding. I have created all of these databases using Python.
The last piece of this, would be being able to loop through all of the filter criteria in column A (which I will not know before-hand and may be anywhere from 10-20 names.
Here is the code I am working with (there are likely some syntax errors in here as well):
Option Explicit
Sub Data()
Dim playername As String
Dim team As String
Dim numFilters As Integer
Dim hits As Integer
Dim src As Worksheet
Dim tgt As Worksheet
Dim filterRange As Range
Dim copyRange As Range
Dim lastRow As Long
Dim i As Integer
team = ThisWorkbook.Sheets(1).Name
numFilters = ActiveSheet.AutoFilter.Filters.Count ' I want this to capture the num of filter criteria for column A
For i = 1 To numFilters
playername = Sheets(team).Filter(i) ' This would be the filter criteria for the given iteration
ActiveSheet.Range("$A$1:$AN$5000").AutoFilter field:=1, Criteria1:=playername
' Create new sheet with name of person
Sheets.Add After:=ActiveSheet
ActiveSheet.Select
ActiveSheet.Name = playername
Set tgt = ThisWorkbook.Sheets(i + 1)
' Perform data analysis (e.g. sum column B of filtered data)
src.AutoFilterMode = False
' Find the last row with data in column A
lastRow = src.Range("A" & src.Rows.Count).End(xlUp).Row
' The range that we are auto-filtering (all columns)
Set filterRange = src.Range("A1:AN" & lastRow)
' Set the range to start in row 2 to prevent copying the header
Set copyRange = src.Range("B2:B" & lastRow)
' Copy the sum of column B to our target cell on the sheet corresponding to this iteration
Application.WorksheetFunction.Sum(copyRange.SpecialCells(xlCellTypeVisible)).Copy tgt.Range("A1")
Next i
End Sub
This is currently failing on the Application.WorksheetFunction.Sum line with the error 'Invalid qualifier'. Thanks for any help and please let me know if something needs clarified.

Extract Row Locations to Use as Reference

I populated an excel sheet with the locations of blank cells in my sheet using suggestions from this post. So I have a Column A filled with locations in the following format
$X$1 or $X2:$X$4.
What I am trying to do is use those row numbers from the column explain above to populate a separate column. I want to use the row numbers as a reference in what to populate for the column. So a Column B looking something like
=$B$1 or =$B$2:$B$4 (took 1 and 2-4 and used it as row number for reference call)
Both columns are referencing a different sheet so please excuse any column naming.
I'm not sure if this is going to require VBA or if I can get away with just using a formula, I expect VBA due to desired specifics. I've looked at post like this and this. But neither of these fully encompass what I'm looking for. Especially since I want it to express all the contents in a $B$2:$B$4 case.
My intuition on how to solve this problem tells me, parse the string from Column A for the 1st number then check if it's the end of the string. If it is, feed it to the reference that populates Column B, if not then find the 2nd number and go through a loop that populates the cell (would prefer to keep all the content in one cell in this case) with each value for each reverence.
i.e.
=$B2
=$B3
=$B4
My question is how do I go about this? How do I parse the string? How do I generate the loop that will go through the necessary steps? Such as using the number as a reference to pull information from a different column and feed it neatly into yet another column.
If (for example) you have an address of $X2:$X$4 then
Dim rng As Range
Set rng = yourSheetReference.Range("$X2:$X$4")
If you want to map that to the same rows but column B then
Set rng = rng.Entirerow.Columns(2)
will do that. note: it's not so clear from your question whether you're mapping X>>B or B>>X.
Once you have the range you want you can loop over it:
For Each c in rng.Cells
'do something with cell "c"
next c
Something like this should work for you:
Sub Tester()
Dim shtSrc As Worksheet, c As Range, rng As Range, c2, v, sep
Set shtSrc = ThisWorkbook.Worksheets("Sheet1") '<< source data sheet
Set c = ActiveSheet.Range("A2") '<<range addresses start here
'process addresses until ColA is empty
Do While c.Value <> ""
'translate range to (eg) Column X
Set rng = shtSrc.Range(c.Value).EntireRow.Columns(24)
sep = ""
v = ""
'build the value from the range
For Each c2 In rng.Cells
v = v & sep & c2.Value
sep = vbLf
Next c2
c.Offset(0, 1) = v '<< populate in colB
Loop
End Sub
Try this code:
Sub Test()
Dim fRng As Range ' the cell that has the formula
Set fRng = Worksheets("sheet1").Range("A1")
Dim tWS As Worksheet 'the worksheet that has the values you want to get
Set tWS = Worksheets("sheet2")
Dim r As Range
For Each r In Range(fRng.Formula).Rows
'Debug.Print r.Row ' this is the rows numbers
Debug.Print tWS.Cells(r.Row, "N").Value 'N is the column name
Next
End Sub

How to loop a dynamic range and copy select information within that range to another sheet

I have already created a VBA script that is about 160 lines long, which produces the report that you see below.
Without using cell references (because the date ranges will change each time I run this) I now need to take the users ID, name, total hours, total break, overtime 1, and overtime 2 and copy this data into sheet 2.
Any suggestions as to how I can structure a VBA script to search row B until a blank is found, when a blank is found, copy the values from column J, K, L, M on that row, and on the row above copy value C - now paste these values on sheet 2. - Continue this process until you find two consecutive blanks or the end of the data...
Even if you can suggest a different way to tackle this problem than the logic I have assumed above it would be greatly appreciated. I can share the whole code if you are interested and show you the data I began with.
Thank you in advance,
J
As discussed, here's my approach. All the details are in the code's comments so make sure you read them.
Sub GetUserNameTotals()
Dim ShTarget As Worksheet: Set ShTarget = ThisWorkbook.Sheets("Sheet1")
Dim ShPaste As Worksheet: Set ShPaste = ThisWorkbook.Sheets("Sheet2")
Dim RngTarget As Range: Set RngTarget = ShTarget.UsedRange
Dim RngTargetVisible As Range, CellRef As Range, ColRef As Range, RngNames As Range
Dim ColIDIndex As Long: ColIDIndex = Application.Match("ID", RngTarget.Rows(1), 0)
Dim LRow As Long: LRow = RngTarget.SpecialCells(xlCellTypeLastCell).Row
'Turn off AutoFilter to avoid errors.
ShTarget.AutoFilterMode = False
'Logic: Apply filter on the UserName column, selecting blanks. We then get two essential ranges.
'RngTargetVisible is the visible range of stats. ColRef is the visible first column of stats.
With RngTarget
.AutoFilter Field:=ColIDIndex, Criteria1:="=", Operator:=xlFilterValues, VisibleDropDown:=True
Set RngTargetVisible = .Range("J2:M" & LRow).SpecialCells(xlCellTypeVisible)
Set ColRef = .Range("J2:J" & LRow).SpecialCells(xlCellTypeVisible)
End With
'Logic: For each cell in the first column of stats, let's get its offset one cell above
'and 7 cells to the left. This method is not necessary. Simply assigning ColRef to Column C's
'visible cells and changing below to CellRef.Offset(-1,0) is alright. I chose this way so it's
'easier to visualize the approach. RngNames is a consolidation of the cells with ranges, which we'll
'copy first before the stats.
For Each CellRef In ColRef
If RngNames Is Nothing Then
Set RngNames = CellRef.Offset(-1, -7)
Else
Set RngNames = Union(RngNames, CellRef.Offset(-1, -7))
End If
Next CellRef
'Copy the names first, then RngTargetVisible, which are the total stats. Copying headers is up
'to you. Of course, modify as necessary.
RngNames.Copy ShPaste.Range("A1")
RngTargetVisible.Copy ShPaste.Range("B1")
End Sub
Screenshots:
Set-up:
Result:
Demo video here:
Using Filters and Visible Cells
Let us know if this helps.

How to build non-consecutive ranges of rows based on cell contents?

I'm just getting started with VBA for Excel. I used VB and Java in college nearly ten years ago and was competent with it then, but am essentially starting over. (Um, not like riding a bike.)
I am trying to understand the methods to build a range that isn't just declared as A1:J34 or whatever. My Googling is challenged in that when searching for 'range' and terms that indicate what I seek, I get an avalanche of hits far more advanced than what I need, mostly hits that don't even address the basic summary info I need.
So, here's the basics of it:
Excel 2011 on Mac.
The sheet has data from A to M, down to 1309.
It's a repeating pattern of heading rows followed by data rows. Ugh. Seems like the person creating the sheet was more thinking about printing from the sheet than the organisation of the data. I need to clean it and 3 more like it up to use in a pivot table, and it's useless in this silly repeating layout.
Heading rows are as follows:
Last Name, First Name, then 10 date cells.
Data rows under the headings are the names, of course, and then a 1 or 0 for attendance.
Anywhere from 20 to 30 names under each heading. Then it repeats. And the dates change every few sets, picking up where the last set left off.
What I need to do right now:
I'm trying to assemble a range into a range variable by adding all the rows beginning with a specific value (in column A). In my case that value is the string "Last Name", so I can have the range variable holding all the cells in all rows that begin with "Last Name". This will then capture all the cells that need to be in date format. (I'm doing it so I can then make sure the date headings are all actually IN date format - because they are NOT all in date format now, many are just 'General' cells.)
My questions:
When telling a range object what it's range IS, how do you feed it cells/rows/columns that are not just a block defined by start and end cells entered by the person writing the code but based on row criteria? Eg: Create a Range that has rows 1, 34, 70, 93, and 128 from columns A to I based on presence of "First Name" in A.
What are the most common methods to do this?
Which of these is best suited to my need and why?
Here's a working example that demonstrates finding the "Last Name" rows, contructing a range object that includes all those rows, and then iterating through that object to search for non-date values. The code could be speeded up greatly by reading the data range into an array of variants and then searching the array for both the last name rows and the "bad dates" within those rows. This is especially true if you have a very large number of rows to check.
Sub DisjointRng()
Dim checkCol As String, checkPattern As String
Dim dateCols()
Dim lastCell As Range, usedRng As Range, checkRng As Range
Dim cell As Variant
Dim usedRow As Range, resultRng As Range, rngArea As Range
Dim i As Long, j As Long
checkCol = "A" 'column to check for "Last Name"
checkPattern = "Last*"
dateCols = Array(3, 5) 'columns to check for date formatting
With Worksheets("Sheet1")
'find the bottom right corner of data range; we determine the used range
'ourselves since the built-in UsedRange is sometimes out-of-synch
Set lastCell = .Cells(.Cells.Find(What:="*", SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, LookIn:=xlFormulas).Row, _
.Cells.Find(What:="*", SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, LookIn:=xlFormulas).Column)
Set usedRng = .Range("A1:" & lastCell.Address)
'the column of values in which to look for "Last Name"
Set checkRng = .Range(checkCol & "1:" & checkCol & usedRng.Rows.Count)
End With
'step down the column of values to check for last name & add
'add found rows to range object
For Each cell In checkRng
If cell.Value Like checkPattern Then
'create a range object for the row
Set usedRow = Intersect(cell.EntireRow, usedRng)
If resultRng Is Nothing Then
'set the first row with "Last Name"
Set resultRng = usedRow
Else
'add each additional found row to the result range object
Set resultRng = Union(resultRng, usedRow)
End If
End If
Next cell
For Each rngArea In resultRng.Areas
'if found rows are continguous, Excel consolidates them
'into single area, so need to loop through each of the rows in area
For i = 1 To rngArea.Rows.Count
For j = LBound(dateCols) To UBound(dateCols)
If Not IsDate(rngArea.Cells(i, dateCols(j))) Then
'do something
End If
Next j
Next i
Next rngArea
End Sub
You can use the Union operator, like this
Dim r As Range
Set r = Range("A1, A3, A10:A12")
or this
Set r = Union(Range("A1"), Range("A3"), Range("A10:A12"))
You can the iterate this range like this
Dim cl as Range
For Each cl in r.Cells
' code cell cl
Next
or this
Dim ar as Range
For each ar in r.Areas
' code using contiguous range ar
For each cl in ar.Cells
' code using cell cl
Next
Next