Excel VBA vlookup using Dates - vba

I am working with three sheets. Worksheet Start Page has dates from A4 to lastrow. I have a Fund trend sheet with dates from A11 to last row. The vlookup is searching for Dates in the Fund trend sheet based on the list of Dates in the start page sheet. The search table range in the Fund trend sheet is Range(A11:C11) to lastrow. When the date is found it offsets (3,0), and that value is presented is sheet Acurred Expenses Range("C7"). This will loop till the lastrow in sheet start page A4.
=VLOOKUP('Start page'!A4,'Fund Trend'!A11:C21,3,0)
=VLOOKUP('Start page'!A5,'Fund Trend'!A12:C22,3,0)
as code i have not been successful:
Sub equity()
Dim Nav_date As Date
Dim equity As Integer
Nav_date = Sheets("Start page").Range("A4")
equity = Application.WorksheetFunction.VLookup(Nav_date,_
Worksheets("Fund Trend").Range("A11:C12"), 3, False)
Sheets("Acurred Expenses").Range("C7") = equity
End Sub

I think this answer can be broken down into three parts: correctly referencing the properties of a Range object, retrieving the last row of data, and using a loop
Correctly referencing the range's value:
The first thing that I noticed is that you are attempting to assign a Date variable as a Range object.
This line:
Nav_date = Sheets("Start page").Range("A4")
Should be:
Nav_date = Sheets("Start page").Range("A4").Value
A Range is an object with has properties and methods. You must explicitly reference what it is about the range you want to get. It's value, it's cell address, etc.
Likewise this incorrect syntax is repeated below. The line:
Sheets("Acurred Expenses").Range("C7") = equity
Should be:
Sheets("Acurred Expenses").Range("C7").Value = equity
EDIT: Per the comments whytheq raises the point of default properties. Technically the code Sheets("Acurred Expenses").Range("C7") = equity is not incorrect, and will work, because the default property of the range is Value. I tend to prefer to be more explicit, but that is my personal preference so I always use Range.Value so there is not ambiguity. Either way should work though!
Retrieving the last row of the worksheet
To find the last used row of the data in the worksheet, we can start at the bottom of the workbook and "look up" until we find the first row (which will correspond to the last row of the data in the worksheet).
This code would be the same as activating the last cell in column A and them pressing CTRL+Shit+↑
Sub LastRow()
Dim lRow As Long
lRow = Cells(Rows.Count, 1).End(xlUp).Row
Debug.Print lRow
End Sub
To reiterate, this starts at the very bottom row and goes all the way up, returning the row number of where it stops. This corresponds to the last value entered in column A. You might need to change A if your data is in a different column.
The loop
Finally, we can put everything we've learned together. After you have lRow which corresponds to your last row in your set of data we can perform a look for the VLOOKUP like so:
Sub equity()
Dim Nav_date As Date
Dim equity As Integer
Dim lRow As Long
Dim i As Long
lRow = Sheets("Start page").Cells(Rows.Count, 1).End(xlUp).Row
For i = 4 To lRow 'Begin in Row 4 of the "Start page" sheet
Nav_date = Sheets("Start page").Range("A" & i).Value
'Tell code to continue even if error occurs
On Error Resume Next
equity = Application.WorksheetFunction.VLookup(Nav_date, _
Worksheets("Fund Trend").Range("A11:C12"), 3, False)
'Check the results of the VLOOKUP, an error number of 0 means no error
If Err.Number = 0 Then
'Here I use i+3 because the data started in row 7 and I assume
'it will always be offset by 3 from the "Start Page"
Sheets("Acurred Expenses").Range("C" & i + 3).Value = equity
End If
'Return to normal error handling
On Error GoTo 0
Next i
End Sub

Related

Weird activecell.offset output

Sub Link()
Dim Turbidity As Long
Dim RawTurbidity As Range
'Sets variables Turbidity being the ActiveCell and RawTurbidity referring to the last captured cell in raw sheets'
Turbidity = ActiveCell.Row
Set RawTurbidity = Sheets("Raw Data").Range("C4").End(xlDown)
'The formula assigning the last captured cell in Raw sheets to the active cell '
Sheet1.Range(Sheet1.Cells(Turbidity, 4), Sheet1.Cells(Turbidity, 4)).Formula = RawTurbidity
End Sub
So this is the code I have and currently it does what it's suppose to do. We have two sheets atm sheet1 and Raw Data An instrument spits out data into column C of Raw data starting wtih C4 and going all the way down. The current code I wrote in essence paste the newest value the instrument spits out to the active cell in sheet1. I have a code on Raw Data that runs the macro only when a change is made to column C4 and lower. And it works exactly how I want it to however...
my question or issue is that when I add activecell.offset(1,0).select in order to have the activecell automatically go to the next row in sheet1 without me moving the mouse the macro copies and paste the same data into the next 4 cells. If I have the intrument spit out the data again than this time it occupies the next 6 rows with the same data.
Joe B, I think you are making this harder than it is.
Last value in a sheet column gets copied to the next open row in a specified column on another sheet? Is that right?
Option Explicit
Sub Link()
Dim ws1 As Worksheet
Dim wsRaw As Worksheet
Dim ws1LastRow As Long ' "Turbidity"
Dim wsRawLastRow As Long ' "RawTurbidity"
' I suggest you just name the sheets using the developer prop window
'It cuts this whole part out as you can call them directly
Set ws1 = ThisWorkbook.Worksheets("Sheet1")
Set wsRaw = ThisWorkbook.Worksheets("Raw Data")
ws1LastRow = ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row 'lets say you are pasting to column A
'ws1LastRow = ws1LastRow + 1
'There you go the next writable cell row, this is wasted code though, see below you just increment when you need it
wsRawLastRow = wsRaw.Cells(wsRaw.Rows.Count, "C").End(xlUp).Row 'This method doesn't care if your data starts in C4
'No formula needed, it is a straight "copy" here, actually faster as its an assignment
ws1.Cells(ws1LastRow + 1, "A").Value = wsRaw.Cells(wsRawLastRow, "C").Value
'the next open cell (defined by row) in your sheet 1 column is equal to the last row of your Raw Data sheet column
End Sub
Issue is that the data in sheet one is not inputted in order. A person may need the data calculated to row 10 and the next calculation needs to be in row 20 hence the need to copy the data into the active cell.
This was my bad for not stating that in the initial post as it's the primary reason for this strange formula.

Excel VBA Code for small scroll while there is a value on the right

I have a Macro that takes data out of 2 reports.
in the second report I have dates that I copy. I need to take a date and subtract from it 14 days
I go to first blank cell in column D, then I want to calculate the formula in column C and scroll down without type how many cells (because it is a macro to a daily basis and the amount of data will change). I want to do this until the end of the data I copied.
In the end I want to copy it as values to column B.
Here is what I have in my code(part of all macro):
'first we go to the buttom of the column
'for NOW - change manually the top of the range you paste to
'Now, paste to OP_wb workbook:
OP_wb.Sheets("Optic Main").Range("D1").End(xlDown).Offset(1, 0).PasteSpecial
Paste:=xlPasteValues
' Calculate Due Date to MFG tools
' it means date we copied from MFG daily minus 14 days
_wb.Sheets("Optic Main").Activate
Range("C1").End(xlDown).Offset(1, 0).Activate
ActiveCell.FormulaR1C1 = "=RC[1]-14"enter code here
You need to loop from the first row to the last row. In general, there are plenty of good ways to define the last row of a given column. Once you have done it, replace the value of lngEndRow and run the following code:
Option Explicit
Public Sub TestMe()
Dim lngStartRow As Long: lngStartRow = 1
Dim lngEndRow As Long: lngEndRow = 100
Dim rngMyRange As Range
Dim rngMyCell As Range
With ActiveSheet
Set rngMyRange = .Range(.Cells(lngStartRow, 5), .Cells(lngEndRow, 5))
End With
For Each rngMyCell In rngMyRange
rngMyCell.FormulaR1C1 = "=RC[1]-14"
Next rngMyCell
End Sub
Then change the ActiveSheet with the correct sheet and the column hardcoded as 5 with the correct one. Run the code above in an empty Excel, to understand what it does. Then change it a bit, until it matches your needs.

How to capture the last non-missing row in a VBA variable for referencing repeatedly later?

I am trying to nativate a spreadsheet that has three columns for each month (Jan-Dec). If the month isn't in the past then the third column should sum the other two (otherwise empty). This should be applied from row 6 to LastRow.
My problem is that I can't work out how to correctly use LastRow. It needs to be defined beforehand using a different column that I can reply on not having any missing values.
I have been trying to capture this row in LastRow but I think I must be doing something fundamentally wrong as I get a type mismatch error for set sh.LastRow:
Sub Prep_datadump()
Dim sh As Worksheet
Dim Dataset as Range
Dim LastRow As Range
Dim Month As Integer
Set sh.Dataset.Range("C6:C" & .Range("C" & .Rows.Count).End(xlUp).Row)
Set LastRow = Dataset.End(xlDown).Row
For Month = 14 To 47 Step 3
If Cells(1, Month) >= Date Then
Sh.Range(Cells(6, Month), Cells(LastRow, Month)).FormulaR1C1 = "=sum(RC[-2],RC[-1])"
End If
Next Month
End Sub
The line you are having a problem with (Set LastRow = Dataset.End(xlDown).Row) attempts to assign the row number of the bottom cell in your Dataset range as an integer to your LastRow variable. As you first define LastRow as a Range the code is unable to do this action as it is a different variable type and so you receive the mismatch error.
Try changing your Dim LastRow As Range line to Dim LastRow As Integer

IF THEN VBA MACRO - Update one column if contents of another = 100%

I have a workbook with "Results" being sheet 3, this being the worksheet I want to use.
I have tried a few formulaes to try and add a macro to do the following:
I have column G with percentages. I then have column I where I would like there to be a result saying TRUE/FALSE where the contents of G are equal to 100%. Column G is formatted to percentage with two decimals.
Some considerations: I have my first row being a Hyperlink to another sheet, then my headings, then the first row of "results". I have 457 rows, if there is a measurement of the range, perhaps it could be on A?
I keep getting this error 9 with my range and have got a bit stuck.
Thanks in advance!
Sub PartialHits1()
Dim rng As Range
Dim lastRow As Long
Dim cell As Range
With Sheet3
lastRow = .Range("G" & .Rows.Count).End(xlUp).Row
Set rng = .Range("G1:G" & lastRow)
For Each cell In rng
If cell.Value = 100
Then
cell.Range("I1:I1").Value = 100
End If
Next
End With
End Sub
(I have hacked this a bit, just was trying to get it to set as 100 instead of the TRUE/FALSE Also was playing around near the Sheet 3 part as I got errors.)
RangeVariable.Range can refer only to a cell within RangeVariable, so you can't refer to column I in this way. Try: .Range("I"&cell.row)=100.
Also your criteria is probably wrong, if you have 100% in a cell it's actual value is 1.
And last question: why do you want to do this with VBA, it would be much more simple with worksheet function =IF(G3=1,100,"")

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