Error Finding multiple date ranges in Excel List - vba

Getting an object variable or with variable not set error. All i'm trying to do is position myself to be able to analyze data from the newStartDate to the newEndDate and compare it to the data in between oldStartDate and oldEndDate. Stuck on the finding the addresses of the dates in the "Master List" Unsure why i'm struggling with this it's a simple task, but I can't seem to solve the error. Any thoughts? I have read the other posts similar to this and can't seem to figure out what i'm doing differently. Thanks in advance Code below:
Note: 1/1/14 is the date set in the range of newStartDate. This date does not exist as business was not conducted until the second. I had found in a post that that the "Set newStartDateFinder" line at the bottom would find the next closest date if the exact date was not found..i'm beginning to think that's not the case since when I attempt to run the debugger it appears to be empty.
Option Explicit
Sub Gather_Calculate_Performance()
'Variable declaration
Dim EBM As Workbook
Dim masterList, controlOut As Worksheet
Dim newStartDate As Range
Dim newEndDate As Range
Dim oldStartDate As Range
Dim oldEndDate As Range
Dim newStartDateFinder As Range
Dim newEndDateFinder As Range
Dim oldStartDateFinder As Range
Dim oldEndDateFinder As Range
'Setting main objects
Set EBM = Workbooks("Monthly Analysis (DEV)")
Set masterList = EBM.Sheets("Master List")
Set controlOut = EBM.Sheets("Control.Output")
'setting main variables
Set newStartDate = controlOut.Range("B" & 5)
Set newEndDate = controlOut.Range("B" & 6)
Set oldStartDate = controlOut.Range("D" & 5)
Set oldEndDate = controlOut.Range("D" & 6)
'Find addresses for dates
Set newStartDateFinder = masterList.Range("A:A").Find(newStartDate, LookIn:=xlValues, LookAt:=xlWhole)
Debug.Print newStartDateFinder.Address
End Sub

Yes, the Find function will look for an exact match, if you set the LookAt argument to xlWhole. (Setting it to xlPart would find matches where the search string exists in part of the cell. So, the Find function won't work in the way you suspected here.
I was thinking you could use a VLOOKUP or MATCH function to find the next closest date to newStartDate but after testing, it didn't work the way I thought it would.
However, this loop while work. If I find a more efficient way, I will update the answer.
'Find addresses for dates
Dim i as Integer
i = 0
Do
Set newStartDateFinder = masterList.Range("A:A").Find(newStartDate.Value +i, LookIn:=xlValues, LookAt:=xlWhole)
i = i + 1
Loop Until Not newStartDateFinder is Nothing
Debug.Print newStartDateFinder.Address

Related

Object Oriented Error -- Referencing Other Worksheets? [duplicate]

This question already has answers here:
Excel VBA, getting range from an inactive sheet
(3 answers)
Closed 6 years ago.
I am using VBA to loop through a list of reference numbers ROID and return the correct full name (ROIDA) from a separate page. The page where the full name is located has the reference number in column D and the full name in column A.
Sub Main()
'Set variable types
Dim WorksheetA As Excel.Worksheet
Dim WorksheetB As Excel.Worksheet
Dim ROID As Range, ROIDA As Range
Set WorksheetA = ActiveWorkbook.Sheets("WorksheetA")
Set WorksheetB = ActiveWorkbook.Sheets("Approval Flows")
'Replacing ROID #s with full Name
'Define range of active requesting offices
Set ROID = WorksheetA.Range(Range("A7"), Range("A7").End(xlDown))
'Define range of attention lines and associated ROIDs
Set ROIDA = WorksheetB.Range(Range("D7"), Range("D7").End(xlDown))
'Loop through ROIDs and replace with ATTN line
For Each ID In ROID
Set Match = ROIDA.Find(ID)
If Not Match Is Nothing Then
ID = Match.Offset(0, -3)
End If
Next ID
End Sub
When I try to run the script, I receive an objected oriented error from this line:
Set ROIDA = WorksheetB.Range(Range("D7"), Range("D7").End(xlDown))
Is this because I'm working with multiple sheets? I am trying hard not to use the activate or select functions.
Yes, if A/B are not active when you run the macro. You need to fully qualify all ranges with sheets. Also better to go the end and work up than go Down in case you have nothing after A7.
With WorksheetA
Set ROID = .Range(.Range("A7"), .Range("A7").End(xlDown))
'Or Set ROID = .Range(.Range("A7"), .Range("A" & Rows.count).End(xlup))
End With
'Define range of attention lines and associated ROIDs
With WorksheetB
Set ROIDA = .Range(.Range("D7"), .Range("D7").End(xlDown))
End With

Match Function in Specific Column Excel VBA

I'm trying to write a program in VBA for Excel 2011 that can search a column (which column that is is determined by another variable) for the number 1 so that it knows where to start an iteration.
Say that the number of the column is given by colnumvar. The only way I can think of is the Match function, which led me to write the following:
Dim rowvar As Integer
rowvar = WorksheetFunction.Match(1,Range(Cells(1,colnumvar),Cells(1000,colnumvar)),0)
This gave me an error, however. After playing around with it some more, I realized that it must not accept the Cells([row],[col]) way of doing it, but rather wants something like Range("A1:A100"). Unfortunately, I can't do it that way, since the program is figuring out what column to look in. Any help for figuring out how to get past this would be greatly appreciated!
What you mean to do is better served with Range.Find.
Dim rngtrg As Range, rngsrc As Range
Dim ws As Worksheet
Set ws = ActiveSheet
Set rngsrc = ws.Range(ws.Cells(1,colnumvar),ws.Cells(1000,colnumvar))
Set rngtrg = rngsrc.Find(1,...)
rowvar = rngtrg.Row
this easy function retreive the positoin of that you find
Function rowvar(ByRef c As Integer) As Integer
Dim keySrc As Integer
keySrc = 22 'wath you want to find
rowvar = WorksheetFunction.Match(keySrc, Range(Cells(1, c), Cells(1000, c)), 0)
End Function
use with rowvar(x)

Excel - Variable in Range

The variable tablelength counts how many items are in a table of mine. I want to select my entire table, but it varies in sizes so my range has to include a variable. I've googled a lot and searched this site (Using variables in Excel range <- that method looked promising but didn't work). Below is a snippet of my code, but includes everything that is relevant.
Private Sub CommandButton1_Click()
Dim shSource As Worksheet
Dim shDest As Worksheet
Dim tablelength As Integer
Set shDest = ThisWorkbook.Sheets("Sheet2")
'here comes some code that determines the value of tablelength, which is 8 in this case
shDest.Range("L" & "4" & ":" & "M" & tablelength).Select
End Sub
I appreciate the help.
edit: the debugger highlights the shDest.Range code.
Unless you need tablelength variable somewhere else in the code, you could try using:
shDest.Range("L4").CurrentRegion.Select
CurrentRegion.Select will select all cells starting from "L4" until it reaches a blank row and column, so providing your tables are surrounded by blank cells this should select the whole table regardless of the size
Here you go, try this:
ActiveSheet.Range(Cells(2, 3), Cells(10, 4)).Select
Taken from http://support.microsoft.com/kb/291308
The first parameter to Cells is the row and the second is the column as a number.
So for you it would look something like this:
shDest.Range(Cells(4, 12), Cells(tablelength, 13)).Select
If it's a proper Table on the spreadsheet, and not just cells formatted to look like a table, you can directly refer to the 'live' size of the table in your code without jumping through all these hoops.
In your VBA code,
The 'Table' is referred to as a ListObject
You can declare a new ListObject, and look up its DataBodyRange.Rows.Count
This should work:
Sub MyMacro()
Dim Tabl As ListObject
Set Tabl = Worksheets("Sheet1").ListObjects("Table1")
MsgBox Tabl.DataBodyRange.Rows.Count
End Sub
You can also set a range variable to refer to the 'Data' range. You need to use the following code.
Dim Rng As Range
Set Rng = Worksheets("Sheet1").ListObjects("Table1").DataBodyRange
Now Rng.Cell(1,1) or Rng.Range("A1") refers to the top left cell of the data body and so on and so forth...

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