I am trying to write code that looks up a batch ID based on the date entered in the "TSDate" field.
I keep getting error 2042 when trying to use the Application.VLookup Function in VBA:
'Timesheet Date
Private Sub TSdate_KeyDown(ByVal KeyCode As _
MSForms.ReturnInteger, ByVal shift As Integer)
Dim TimesheetDate As Date
Dim batch As Variant
Dim DRange As Range
Set DRange = Range("Table_PayPeriods")
If KeyCode = 13 Or KeyCode = 9 Then
TSDate.Value = Format(TSDate.Value, "dd-mmm-yy")
TimesheetDate = TSDate.Value
batch = Application.VLookup(TSDate.Value, DRange, 2, 0)
MsgBox (DRange(2, 2))
BatchID.Text = batch
End If
End Sub
The messagebox proves that the data being looked up is being pulled properly, the problem is I am getting the error in the "batch" variable.
Any help would be appreciated. Thanks!
Application.VLookup will return Error 2042 when the lookup value is not found.
You need to test for error, and handle appropriately:
If KeyCode = 13 Or KeyCode = 9 Then
TSDate.Value = Format(TSDate.Value, "dd-mmm-yy")
TimesheetDate = TSDate.Value
batch = Application.VLookup(TSDate.Value, DRange, 2, 0)
If IsError(batch) Then
'Do something
Else
MsgBox (DRange(2, 2))
BatchID.Text = batch
End If
End If
As for why the value is not found, it's not possible to answer without more detail from you regarding the input data and respective formats -- perhaps the value really doesn't exist, or perhaps it appears to exist but really does not: (generally I would expect a string will not match a date type and vice-versa).
If the cells contain Date type values (even if they are formatted to look like strings, an Error is expected. In this case, convert the string input (TSDate.Value) to a Date type, and convert that to a Long type and do the Vlookup with its long numeric equivalent:
batch = Application.VLookup(CLng(CDate(TSDate.Value)), DRange, 2, 0)
You'll still need Error handling in the event that the date value literally doesn't exist in the table.
Related
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
I'm a newcomer to VBA so was hoping someone with more experience could please explain why I receive a
Type Mismatch error
against the following:
Private Sub cbbWeek_Change()
txtWeekEnding = Application.VLookup(cbbWeek.Value, Worksheets("Formulas").Range("Q1:R53"), 2, False)
End Sub
I'm not sure if it's relevant but;
Column Q contains numbers from 1 (in cell Q1) to 52 (in cell Q52)
Column R contains dates formatted to dd/mm/yyyy
To see whether VLookup returns an error or not - assign the returned value to a variable. Check if the variable is an error, and if it is not an error - assign it to txtWeekending:
Private Sub TestMe()
Dim checker As Variant
Dim txtWeekending As Variant
checker = Application.VLookup("vityata", Range("A1:C53"), 2, False)
If Not IsError(checker) Then
Debug.Print checker
txtWeekending = checker
Else
Debug.Print checker
End If
End Sub
This is an article from CPearson, concerning the same problem.
I have created my own function to determine count the values in between to given values in increments of 30 as seen here
Function InBetween(First As Integer, Last As Integer)
Dim i As Long, F As String, a() As String
F = First
For i = First + 30 To Last Step 30
F = F & "|" & i
Next i
InBetween = F
End Function
When I use this function, I currently have it returning the result array in the cell the formula was entered into in the format of "1|2|3|4". Is there a way I can get this array to populate into the cell below the one containing the formula?
Note: I don't want the formula in the cell as I need to refer to the cell in a future equation that will use the result and not the equation.
This was surprisingly difficult. At first I tried calling a sub from the function to affect the cell below using application.caller but this always returned a #value error. It seems a UDF can't run anything that affects the worksheet.
Eventually I came up with this:
Create a worksheet change event by pasting this into the worksheet object in vb:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error Resume Next
If Left(Target.Offset(-1, 0).Formula, 10) = "=InBetween" Then Call DoX(Target.Offset(-1, 0), InBetween(10, 60))
On Error GoTo 0
End Sub
Then paste this into a module
Sub DoX(r As Range, val As String)
Sheets(r.Parent.Name).Cells(r.Row, r.Column) = ""
Sheets(r.Parent.Name).Cells(r.Row + 1, r.Column) = val
End Sub
Then use your function as normal, but remember to hit return after you enter it so the active cell is the cell below where you entered the formula.
I am trying to validate a date that is entered into a textbox on a user form. When I enter for example 23/7/15 the code works fine and gives me a value as the date entered is valid, when I enter 32/7/2015 I get a message that I program telling the user that they have entered a wrong date but if I enter 32/7/15 it sets the date in the code to some date in 1932 and as this is a valid date it does not throw the error. Below is the code that I am using. Is there anyway to validate 32/7/15?
Private Function errorCheckingDate(box1, message) As Boolean '<============= Returns a True or a False
If Not IsDate(box1) Then '<============================================= Checks to see if entered value is date or not
MsgBox message, vbExclamation, "Invalid Selection" '<=============== Displays a messgae box if not date
box1.SetFocus '<==================================================== Puts cursor back to the offending box
errorCheckingDate = True '<========================================= Sets function to True
End If
End Function
box1 is just the value of the textbox once it has been converted to a date. Below is the conversion
secondSelectedStr = Format(DateTextBox.value, "dd-mm-yy") '<===== Convert to correct format
Any help would be great.
In addition to the ordering issue, VBA will also try to adapt invalid date ranges to a proper date. For example, this has always been a neat technique to get the X day of the year:
d = DateSerial(2015, 1, X)
X can be really any number here (up to the limits of the data type, of course). If X is 500, it will produce the date 2016-5-14. So the mm/dd vs dd/mm vs yy/mm/dd issue plus the fact that VBA accepts numbers outside valid ranges are both troublesome.
Without range-checking each date component (where you have to consider leap years and whatnot), I think the best solution is to just have VBA create your date and then test it to ensure it's what you're expecting. For example:
' Get the date components (assuming d/m/yy)...
Dim a As Variant
a = Split(strDate, "/")
' Need 3 components...
If UBound(a) <> 2 Then MsgBox "Invalid date": Exit Sub
' Create the date. This will hardly ever fail.
' VBA will create some kind of date using whatever numbers you throw at it.
Dim d As Date
d = DateSerial(a(2), a(1), a(0))
' Make sure the date VBA created matches what we expected...
If Day(d) <> CInt(a(0)) Then MsgBox "Invalid day": Exit Sub
If Month(d) <> CInt(a(1)) Then MsgBox "Invalid month": Exit Sub
If Right$(Year(d), 2) <> a(2) Then MsgBox "Invalid year": Exit Sub
You could throw that into a subroutine (you may have noticed the Exit Sub statements) to validate the date that gets entered. The TextBox_Exit event would work well. That way, you could set Cancel = True to prevent the user from leaving the textbox with an invalid date.
Thanks for the answers guys but I need to accommodate for leap years and other things. As the isdate function works if the date is entered in the form 1/1/2015 I have just used the len() function to make sure that the date entered is of a certain length. Below is the code that I used. Thanks for you help once again. :)
Private Function errorCheckingDate(box1, message) As Boolean '<============= Returns a True or a False
If Not IsDate(box1) Or Len(box1) < 8 Or Len(box1) > 10 Then '<============================================= Checks to see if entered value is date or not
MsgBox message, vbExclamation, "Invalid Selection" '<=============== Displays a messgae box if not date
box1.SetFocus '<==================================================== Puts cursor back to the offending box
errorCheckingDate = True '<========================================= Sets function to True
End If
End Function
Maybe instead of using IsDate you could use your own custom function likened to it.
Function isValidDate(d as String) as Boolean
Dim parts as Variant
Dim months29 as variant
Dim months30 as variant
Dim months31 as variant
months29 = Array(2)
months30 = Array(4, 6, 9, 11)
months31 = Array(1, 3, 5, 7, 8, 10, 12)
parts = Split(d, "-")
if parts(2) mod 4 = 0 then
daysLeapYear = 28
else:
daysLeapYear = 29
end if
if ((IsInArray(parts(0), months29) and parts(1) <= daysLeapYear) or _
(IsInArray(parts(0), months30) and parts(1) < 31) or _
(IsInArray(parts(0), months31) and parts(1) < 32)) and _
parts(2) < 16 then
isValidDate = True
else:
isValidDate = False
end if
end function
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
This should cover most cases. Not sure how detailed you feel you need to be. IsInArray courtesy of #JimmyPena.
I'm writing a script that will count a numbers of days between few separate dates. I have a data in cell like:
1-In Progress#02-ASSIGNED TO TEAM#22/01/2013 14:54:23,4-On
Hold#02-ASSIGNED TO TEAM#18/01/2013 16:02:03,1-In Progress#02-ASSIGNED
TO TEAM#18/01/2013 16:02:03
That's the info about my transaction status. I want to count the numbers of days that this transaction was in "4-On Hold". So in this example it will be between 18/01/2013 and 22/01/2013.
I wrote something like this(sorry for ma native language words in text)
Sub Aktywnywiersz()
Dim wiersz, i, licz As Integer
Dim tekstwsadowy As String
Dim koniectekstu As String
Dim pozostalytekst As String
Dim dataztekstu As Date
Dim status4jest As Boolean
Dim status4byl As Boolean
Dim datarozpoczecia4 As Date
Dim datazakonczenia4 As Date
Dim dniw4 As Long
wiersz = 2 'I start my scrypt from second row of excel
Do Until IsEmpty(Cells(wiersz, "A")) 'this should work until there is any text in a row
status4jest = False 'is status 4-On Hold is now in a Loop
status4byl = False 'is status 4-On Hold was in las loop
dniw4 = 0 ' numbers od days in 4-On Hold status
tekstwsadowy = Cells(wiersz, "H").Value2 'grabing text
tekstwsadowy = dodanieprzecinka(tekstwsadowy) 'in some examples I had to add a coma at the end of text
For i = 1 To Len(tekstwsadowy)
If Right(Left(tekstwsadowy, i), 1) = "," Then licz = licz + 1 'count the number of comas in text that separates the changes in status
Next
For j = 1 To licz
koniectekstu = funkcjaliczeniadni(tekstwsadowy) 'take last record after coma
Cells(wiersz, "k") = koniectekstu
dataztekstu = funkcjadataztekstu(koniectekstu) 'take the date from this record
Cells(wiersz, "m") = dataztekstu
status4jest = funkcjaokreslenia4(koniectekstu) 'check if there is 4-On Hold in record
Cells(wiersz, "n") = status4jest
If (status4byl = False And staus4jest = True) Then
datarozpoczecia4 = dataztekstu
status4byl = True
ElseIf (status4byl = True And staus4jest = False) Then
datazakonczenia4 = dataztekstu
status4byl = False 'if elseif funkcion to check information about 4-On Hold
dniw4 = funkcjaobliczeniadniw4(dniw4, datazakonczenia4, datarozpoczecia4) 'count days in 4-On Hold
Else
'Else not needed...
End If
tekstwsadowy = resztatekstu(tekstwsadowy, koniectekstu) 'remove last record from main text
Next
Cells(wiersz, "L") = dniw4 ' show number of days in 4-On Hold status
wiersz = wiersz + 1
Loop
End Sub
Function funkcjaliczeniadni(tekstwsadowy As String)
Dim a, dl As Integer
dl = Len(tekstwsadowy)
a = 0
On Error GoTo errhandler:
Do Until a > dl
a = Application.WorksheetFunction.Find(",", tekstwsadowy, a + 1)
Loop
funkcjaliczeniadni = tekstwsadowy
Exit Function
errhandler:
funkcjaliczeniadni = Right(tekstwsadowy, dl - a)
End Function
Function dodanieprzecinka(tekstwsadowy As String)
If Right(tekstwsadowy, 1) = "," Then
dodanieprzecinka = Left(tekstwsadowy, Len(tekstwsadowy) - 1)
Else
dodanieprzecinka = tekstwsadowy
End If
End Function
Function resztatekstu(tekstwsadowy, koniectekstu As String)
resztatekstu = Left(tekstwsadowy, Len(tekstwsadowy) - Len(koniectekstu))
End Function
Function funkcjadataztekstu(koniectekstu As String)
funkcjadataztekstu = Right(koniectekstu, 19)
funkcjadataztekstu = Left(funkcjadataztekstu, 10)
End Function
Function funkcjaobliczeniadniw4(dniw4 As Long, datazakonczenia4 As Date, datarozpoczecia4 As Date)
Dim liczbadni As Integer
liczbadni = DateDiff(d, datarozpoczecia4, datazakonczenia4)
funkcjaobliczaniadniw4 = dniw4 + liczbadni
End Function
Function funkcjaokreslenia4(koniectekstu As String)
Dim pierwszyznak As String
pierwszyznak = "4"
If pierszyznak Like Left(koniectekstu, 1) Then
funkcjaokreslenia4 = True
Else
funkcjaokreslenia4 = False
End If
End Function
And for now I get
Run-time error "13"
in
dataztekstu = funkcjadataztekstu(koniectekstu) 'take the date from this record
I would be very grateful for any help.
You are getting that error because of Type Mismatch. dataztekstu is declared as a date and most probably the expression which is being returned by the function funkcjadataztekstu is not a date. You will have to step through it to find what value you are getting in return.
Here is a simple example to replicate that problem
This will give you that error
Option Explicit
Sub Sample()
Dim dt As String
Dim D As Date
dt = "Blah Blah"
D = getdate(dt)
Debug.Print D
End Sub
Function getdate(dd As String)
getdate = dd
End Function
This won't
Option Explicit
Sub Sample()
Dim dt As String
Dim D As Date
dt = "12/12/2014"
D = getdate(dt)
Debug.Print D
End Sub
Function getdate(dd As String)
getdate = dd
End Function
If you change your function to this
Function funkcjadataztekstu(koniectekstu As String)
Dim temp As String
temp = Right(koniectekstu, 19)
temp = Left(temp, 10)
MsgBox temp '<~~ This will tell you if you are getting a valid date in return
funkcjadataztekstu = temp
End Function
Then you can see what that function is returning.
I tried running your code, but it is a little difficult to understand just what it is that you want to do. Part of it is the code in your language, but the code is also hard to read beacuse of the lack of indentation etc. :)
Also, I do not understand how the data in the worksheet looks. I did get it running by guessing, though, and when I did I got the same error you are describing on the second run of the For loop - that was because the koniectekstu string was empty. Not sure if this is your problem, so my solution is a very general.
In order to solve this type of problem:
Use Option Explicit at the top of your code module. This will make you have to declare all variables used in the module, and you will remove many of the problems you have before you run the code. Eg you are declaring a variable status4jest but using a different variable called staus4jest and Excel will not complain unless you use Option Explicit.
Declare return types for your functions.
Format your code so it will be easier to read. Use space before and after statements. Comment everything! You have done some, but make sure a beginner can understand. I will edit you code as an example of indentation.
Debug! Step through your code using F8 and make sure all variables contain what you think they do. You will most likely solve your problem by debugging the code this way.
Ask for help here on specific problems you run into or how to solve specific problems, do not send all the code and ask why it is not working. If you break down your problems into parts and ask separately, you will learn VBA yourself a lot faster.
A specific tip regarding your code: look up the Split function. It can take a string and make an array based on a delimiter - Example: Split(tekstwsadowy, ",") will give you an array of strings, with the text between the commas.
Did I mention Option Explicit? ;)
Anyway, I hope this helps, even if I did not solve the exact error you are getting.