Why is VLookup in VBA failing with runtime error 1004? - vba

Spreadsheet "Sheet3" looks like this:
S&P 500 DJIA
1/1/1991 795.4476 2973.09
1/2/1991 786.3856 2947.1
1/3/1991 775.4636 2905.19
1/4/1991 773.5364 2896.8
1/7/1991 760.2996 2847.9
1/8/1991 759.0029 2832.81
1/9/1991 750.8416 2788.67
1/10/1991 758.1719 2820.8
Also Cell "F2" is literally a copy and paste of 1/7/1991 cell.
VBA Code looks like this:
Sub badlook3()
Dim BenchSI As Variant
Dim BRange As Range
Dim SIDate As Date
Set BRange = Worksheets("Sheet3").Range("A2:C9")
MsgBox BRange.Address
SIDate = Worksheets("Sheet3").Range("F2").Value
BenchSI = Application.WorksheetFunction.VLookup(SIDate, BRange, 2, True)
End Sub
I am getting the "Unable to get the VLOOKUP property of the WorkSheet Function class" error.
What am I missing here? Column A is in the right order. They are dates. What does Excel want from me?

The problem is with using SIDate as Date(Visual Basic date type)
My guess would be that visual basic date type and excel date type do not match, that's why you're getting an error
Instead declare SIDate as a Range, and it will work
Here's the code:
Sub badlook3()
Dim BenchSI As Variant
Dim BRange As Range
Dim SIDate As Range
Set BRange = Worksheets("Sheet3").Range("A2:C9")
MsgBox BRange.Address
Set SIDate = Worksheets("Sheet3").Range("F2")
BenchSI = Application.WorksheetFunction.VLookup(SIDate, BRange, 2, True)
End Sub

You are asking vLookup to return on a 2 column range, against a 1 column range. Change BRange = "A2:B9" to make your vLookup pick up the S&P Value.
Alternatively, you can change the range to A2:C9 and change the 2 to a 3 in your vLookup and get the DJ average.
In short, vLookup can only return a column reference to the greatest amount of columns in a range. It can return the 1st, 2nd, 0r 3rd column reference in a 3 column range, but not the 4th, because there is no 4th column.

Related

Excel VBA with matches and index

I've written some VBA code with two matches and index formula. I need to pick the unique value from a sheet and compare it with the other sheet. However it is not working. I get some errors. (unable to get the match property of the worksheetfunction class vba - this is the error)
Here is my code :
Sub Post_Attendance()
Sheets("DB").Activate
'On Error Resume Next
Dim myvalue As String
Dim mydate As String
Dim mypost As String
(the date value entered in a cell)
Dim Dt As String
Dt = Range("C7").Value
(the unique id entered in a cell)
Dim empid As String
empid = Range("C8").Value
(activating another worksheet , from a cell value)
Dim strWsName As String
strWsName = Sheets("DB").Range("A7")
Sheets(Left(strWsName, 3)).Select
(match function to find the row and columns number for indexing)
mydate = WorksheetFunction.Match(Dt, Range("B1:Q1"), 0)
myvalue = WorksheetFunction.Match(empid, Range("A5:A500"), 0)
mypost = WorksheetFunction.Index(Range("B2:Q6"), myvalue, mydate)
End Sub
First off, WorksheetFunction.Match never returns a string; it either returns a number (a long integer) or an error. It is not the value from the match, it is the row or column number where the match was found.
Next, you cannot catch an #N/A error from no match with WorksheetFunction.Match but you can catch it with Application.Match into a variant.
Real dates are numbers, not strings. The raw underlying value is another long integer; e.g. a positive whole number with no decimal portion. If you had time or a datetime then you would have a decimal portion.
Resolve and reference your parent worksheet properly; do not rely upon Select or Activate.
The number returned from MATCH is the position within the range of cells searched. You are looking for a row number from row 5 to row 500 then using that to find a row within row 2 to 6; any match above row 9 (match returning 6 or above) in the original is going to be out-of-range.
If the empid values are numbers then deal with numbers; you cannot find a match to a true number from text-that-looks-like-a-number; e.g. 99 <> "99". I'm going to assume that empid should be alphanumeric and not a true number but given the errors with the previous variable assignments, it is up to you to determine the correct assignment.
Here is my best guess at an error controlled sub procedure (given that you have shown no sample data).
Option Explicit
Sub Post_Attendance()
'On Error Resume Next
Dim myvalueRow As Variant, mydateCol As Variant, dt As Long, empid As String, mypost As Variant
dt = Worksheets("DB").Range("C7").Value2
empid = Worksheets("DB").Range("C8").Value2
With Worksheets(Left(Worksheets("DB").Range("A7").Value2, 3))
'locate the column for the date
mydateCol = Application.Match(dt, .Range("B1:Q1"), 0)
If IsError(mydateCol) Then _
mydateCol = Application.Match(CStr(Worksheets("DB").Range("C7").Value2), .Range("B1:Q1"), 0)
If IsError(mydateCol) Then
Debug.Print "dt not found in row 1"
Exit Sub
End If
'locate the row for the value
myvalueRow = Application.Match(empid, .Columns("A"), 0)
If IsError(myvalueRow) Then
Debug.Print "empid not found in column A"
Exit Sub
End If
mypost = Application.Index(.Range("B:Q"), myvalueRow, mydateCol)
End With
End Sub

VBA Macro Compile Error

I'm attempting to write a simple VBA macro that will take the active cell's column and the user's input to add a range of cells on a single row together. The range is calculated by adding the integer the user inputs to the active column and that is the end column. The problem is that it gives me a "Compile Error: Invalid Qualifier" when I run it, and gets angry at the 'total' line.
Here is my code. I'm just starting in VBA, but it can't be that hard....right?
Sub Food()
Dim first As Variant
Dim last As Integer
Dim days As Integer
Dim month As Range
Dim total As Double
first = ActiveCell.Column
days = InputBox("Days in the month?")
last = first + days
Set month.Value = Range(Cells(first, 4), Cells(last, 4))
total.Value = WorksheetFunction.Sum(month)
Worksheets(1).Cells(1, 13).Value = total
End Sub
Looks like a syntax error. Try dropping the .value on month and total.
Sub Food()
Dim first As Variant
Dim last As Integer
Dim days As Integer
Dim month As Range
Dim total As Double
first = ActiveCell.Column
days = InputBox("Days in the month?")
last = first + days
Set month = Range(Cells(first, 4), Cells(last, 4))
total = WorksheetFunction.Sum(month)
Worksheets(1).Cells(1, 13).Value = total
End Sub
If you want to get the value of a range into a variable, you put the .value with the range instead of the variable. For example:
x = cells(1,2).value
The .value property returns the value in a range object. So it doesn't make sense to use it on a function and will cause excel to throw and error if you try. It also doesn't make sense to try to set the month variable as a value instead of a range since that would just make it an array. If you want month to be an array you would want to set it as a variant instead of a range.
For more information on the .value property, see this link:
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/range-value-property-excel

Simple moving average range in Excel-VBA

This code is just to calculate simple moving average. Opened an excel, created dummy array in C row from 1 to 20. I want to create a function for eg: SMA(C7,3) = which should give average of C5:C7.
Coming back to VBA after long time, not able to figure whats the error in the below code.
Function sma1(rng As Range, N As Integer)
Set rng = rng.Resize(-N + 1, 0)
sma1 = Application.WorksheetFunction.average(rng)
End Function
avoid using a cell name as a function
fixed the RESIZE()
used an internal range variable
Function smal(rng As Range, N As Integer) As Variant
Dim rng2 As Range
Set rng2 = rng.Resize(N, 1)
smal = Application.WorksheetFunction.Average(rng2)
End Function
EDIT#1:
Based on Scott's comment:
Function smal(rng As Range, N As Integer) As Variant
Dim rng2 As Range
Set rng2 = rng.Offset(1 - N, 0).Resize(N, 1)
smal = Application.WorksheetFunction.Average(rng2)
End Function
I assume you want the column along side it to give you're SMA (as shown below?):
If so, the below will do it and drag it autocomplete it to the bottom of you column C array:
Sub SMA3()
Range("D7").FormulaR1C1 = "=AVERAGE(R[-2]C[-1]:RC[-1])" 'This is a relative reference (left one cell and up two cells) - This give your three inputs
Range("D7").AutoFill Destination:=Range("D7:D" & Range("C1048576").End(xlUp).Row) 'Autofills the SMA
End Sub
Just an FYI this can be done with existing formula:
=IF(ROW(C1)<$E$1,"",AVERAGE(INDEX(C:C,ROW(C1)-$E$1+1):C1))
E1 contains the number of rows to include.

VBA Excel string datatype variable assignment

I am trying to set a value for CurrentClient Variable i have defined below and then using it to count how many times it Occurs in a range. I am not sure what am i doing wrong here . It is giving me error: "runtime error 9 subscript out of range" on step where it is assigning the value from Sheet 2 cell A2 to Currentclient.
Please help.
Sub GetValue()
Dim ClientCnt As Integer
Dim CurrentClient As String
CurrentClient = Sheets("Sheet 2").Range("A2").Text
ClientCnt = Application.WorksheetFunction.CountIf(Range("A:A"), CurrentClient)
End Sub
Use Sheet2
rather than
Sheet 2

lookup a number and increment value in another cell within same row

I would like to create a macro in excel that lets me increment the counts of a part whenever I press a command button.
Currently, my concept is to use vlookup to get the existing counts for that part using the following. However, it does not increment the actual counts value in the cell, which is what I want. I suspect it's cos vlookup is only used to return a value within the cell, but the cell is not activated in the process for actual increment. Can someone please advise how I can correct it? I'm still new to vba. Thanks!!! :)
E.g. Vlookup finds C1value in Cell A5 of Sheets("Location"). It will automatically increment the value in Cell C5 by 1.
Sub FindAddTools()
Dim C1Qnty As Double
C1value = Sheets("Issue").Range("D11")
Sheets("Location").Activate
C1Qnty = WorksheetFunction.VLookup(C1value, Range("A:D"), 3, False)
C1Qnty = C1Qnty + 1
End Sub
ADD ON: an add-on to my original question. I was wondering if it is possible to do the same for an entire range?
E.g. C1value is now a range of Sheets("Issue").Range("D11:D20"). I want to find all values within this range in Sheets("Location") and increment their corresponding counts in Column C.
Is there a way to do this without repeating the same procedure for all cells of the range?
Thanks! :)
Here's my shot at it. If the value isn't matched nothing happens:
Sub FindAddTools()
Dim RangeToMatch As Excel.Range
Dim cell As Excel.Range
Dim C1Value As Variant
Dim C1Row As Variant
Set RangeToMatch = Sheets("Issue").Range("D2:D11")
For Each cell In RangeToMatch
C1Value = cell.Value
With Sheets("Location")
C1Row = Application.Match(C1Value, .Range("A:A"), 0)
If Not IsError(C1Row) Then
.Range("C" & C1Row).Value = .Range("C" & C1Row).Value + 1
End If
End With
Next cell
End Sub
I edited it so that it cycles through a range of cells to match. That range is set to D2:D11 above.
Based on your comments, I think this should do it.
NB: you don't have to Activate worksheets to perform the functions referencing their cells/ranges.
Sub FindAddTools()
Dim shIssue as WOrksheet: Set shIssue = Sheets("Issue")
Dim shLoc as Worksheet: Set shLoc = Sheets("Location")
Dim allC1Values as Range
Dim C1Value as Variant
Dim C1Qnty As Double
Dim foundRow as Long
Set allC1Values = shIssue.Range("D11:D100") '## Modify as needed.
For each C1Value in allC1Values.Cells
C1Qnty = WorksheetFunction.VLookup(C1value, shLoc.Range("A:D"), 3, False)
C1Qnty = C1Qnty + 1
foundRow = WorksheetFunction.Match(c1Value,shLoc.Range("A:A"),False)
shLoc.Range("C" & foundRow).Value = CqQnty
Next
End Sub
Be careful with this. You're immediately writing to the same cell you just "found" with the VLOOKUP function, so, obviously if you run this macro again, you're going to increment it again. But, this may be the desired functionality, if so, no problem.
NOTE: There is no error trapping for if C1Value is not found in the VLOOKUP or MATCH functions.