VBA - Excel : Vlookup crashes my program when no match found - excel

In my program, the user types a Zip Code and gets as an output information related to the Zip Code (province, city, district). To do this, I use the Vlookup function.
So, the user :
Types a Zip code in the main sheet
The program search in a database (in another sheet) in which Zip Code are associated to City, Province, District.
When there is a match, it sends the result to the main pages, so the user can get a city, province, district just by typing the Zip Code. Quite simple process.
I use this code to do so :
If Range("J9").Value <> "N/A" Then 'if there is actually a zip code entered by the user (if not, it will be "N/A")
cityZip = Application.WorksheetFunction.VLookup(sMain.Range("J9").Value,
sZipCodes.Range("B2:E864"), 3, False)
barangayZip = Application.WorksheetFunction.VLookup(sMain.Range("J9").Value,
sZipCodes.Range("B2:E864"), 2, False)
provinceZip = Application.WorksheetFunction.VLookup(sMain.Range("J9").Value,
sZipCodes.Range("B2:E864"), 4, False)
sMain.Range("J7").Value = provinceZip
sMain.Range("J13").Value = cityZip
sMain.Range("J16").Value = barangayZip
Else
End If
It works perfectly when there is a Zip Code which is in my database. But if not, it crashes the execution of the program and I have an error message (like "execution error '1004', unable to read the Vlookup ...).
How to modify my code to just say that if there is no match, then it should just do nothing? I don't know how to introduce this request in a Vlookup function.
Thanks in advance !
EDIT : here is my new code, after following Tim Williams suggestion :
'Using Zip Code
If Range("J9").Value <> "N/A" Then
provinceZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907"), 4, False)
If IsError(provinceZip) = False Then
cityZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907"), 3, False)
barangayZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907"), 2, False)
sMain.Range("J7").Value = provinceZip
sMain.Range("J13").Value = cityZip
sMain.Range("J16").Value = barangayZip
Else
'do nothing
End If
End If
My error is on this line :
provinceZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907"), 4, False)
=> Error 1004, invalid number of arguments

You should read up on VBA error handling. A source such as http://www.cpearson.com/excel/errorhandling.htm might help. That said, try the following code.
You want something like:
Public Function SafeVlookup(lookup_value, table_array, _
col_index, range_lookup, error_value) As Variant
On Error Resume Next
Err.Clear
return_value = Application.WorksheetFunction.VLookup(lookup_value, _
table_array, col_index, range_lookup)
If Err <> 0 Then
return_value = error_value
End If
SafeVlookup = return_value
On Error GoTo 0
End Function
In your code you might call it like:
cityZip = SafeVlookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E864"), 3, _
False, "")
The last parameter is the default value to return if the vlookup failed. So in this example it'd return an empty string.

I usually wrap the vlookup() with an iferror() which contains the default value.
The syntax would be as follows:
iferror(vlookup(....), <default value when lookup fails>)
You can also do something like this:
Dim result as variant
result = Application.vlookup(......)
If IsError(result) Then
' What to do if an error occurs
Else
' what you would normally do
End if

You changed from Vlookup to Lookup, which has less arguments. Using only 2 arguments, you should be fine: provinceZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907") )

SafeVlookup is a good function.
I am still learning VB.
I changed like this and it works for me.
Function SafeVlookup(lookup_value, _
range_lookup, col_index, error_value) As Variant
.....
return_value = Application.WorksheetFunction.vlookup(lookup_value, _
range_lookup, col_index, error_value)
....
End Function
Hope I can use it like this.

Related

How to loop through XML-nodes and validate if values exists?

I have through an API fetched my data as an XML, and I wish to cycle through nodes (there are several of the same type) and add them to certain fields/a table.
Example from the XML-file:
<HistRating
xmlns="">
<EndrAr>2020</EndrAr>
<EndrMnd>7</EndrMnd>
<Rating>A</Rating>
</HistRating>
<HistRating
xmlns="">
<EndrAr>2019</EndrAr>
<EndrMnd>6</EndrMnd>
<Rating>A</Rating>
</HistRating>
I have tried the following format (at this point the XML I need is in a string in xmlDoc xmlDoc = CreateObject("MSXML2.DOMDocument.6.0"). Fully aware that this is not a really "sexy" way to write it, but I'm new at this game:
Set nodeXML = xmlDoc.getElementsByTagName("EndrAr")
Range("G1").Value = nodeXML(1).Text
Range("H1").Value = nodeXML(2).Text
Range("I1").Value = nodeXML(3).Text
Set nodeXML = xmlDoc.getElementsByTagName("EndrMnd")
Range("G2").Value = nodeXML(1).Text
Range("H2").Value = nodeXML(2).Text
Range("I2").Value = nodeXML(3).Text
Set nodeXML = xmlDoc.getElementsByTagName("Rating")
Range("G3").Value = nodeXML(1).Text
Range("H3").Value = nodeXML(2).Text
Range("I3").Value = nodeXML(3).Text
This works great as long as all three items are there. Unfortunately that is not given. If it is a new company i.e. (3) wont exist (there is one line per year above), and I would like to either set the cell to Blank or No value or something.
The result from when I run the above code:
But if I try to add a line 4 to test what happens if value does not exists I get the following (for obvious reasons)
What I would love some help with is:
Can I by some "magic" add a ifmissing (tried it, but could not get it to work)?
Other ways to add a if variable is not found, input following into cell
Or are there a complete different way I should have solved this?
This is to add accounting data from last X available years (where X is ie 4, or less if not 4 is available) from 30 nodes.
You could use an Error trapping Function. Note in the code below we choose not to use the returned boolean.
Dim myTest as String
.
.
TryReadingXmlNode nodeXML,1, myText
Range("G1").Value = myText
.
.
Public Function TryReadingXmlNode(ByVal ipNode as object, ByVal ipIndex as Long, ByRef opText as string) as boolean
On Error Resume Next
opText=ipNode.Item(ipIndex).Text
TryReadingXmlNode=Len(opText)>0
If err.number>0 then opText="NoValue"
on Error Goto 0
End Function
Start by querying all of the HistRating elements, then loop over that collection:
Const MAX_YEARS As Long = 4
Dim ratings, rating, c As Range, i as Long
Set c= Range("A1")
Set ratings = xmlDoc.getElementsByTagName("HistRating")
For Each rating in ratings
c.offset(0, i) = rating.getElementsByTagName("EndrAr")(0).Text
c.offset(1, i) = rating.getElementsByTagName("EndrMnd")(0).Text
c.offset(2, i) = rating.getElementsByTagName("Rating")(0).Text
i = i + 1
If i >= MAX_YEARS Then Exit For 'exit if processed enough nodes
Next rating

Type mismatch when checking the value of a cell

I have this code hitting
error 13 :Type Mismatch
whenever I enter or change the value in C59, C59 contains this formula =ROUNDUP(D59/C57,0). I am guessing its because of the different variable but do not know how to solve it, any kind assistance is greatly appreciated!
If Range("C59").Value = "0" Then
Sheets("InvPL").Rows("78:79").EntireRow.Hidden = True
Else
Sheets("InvPL").Rows("78:79").EntireRow.Hidden = False
End If
You will want to make sure C57 is not 0 as you are dividing by it, in which case C59 will be #DIV/0! (an error)
You cannot check the .Value property of a cell if it contains an error.
Check cell C59 for the error first:
If IsError(Worksheets("InvPL").Range("C59")) Then
Msgbox "C59 contains an error. If C57=0 then it is a divide by zero error."
Else
If Range("C59").Value = "0" Then
Sheets("InvPL").Rows("78:79").EntireRow.Hidden = True
Else
Sheets("InvPL").Rows("78:79").EntireRow.Hidden = False
End If
End If
Or a cleaner version, using a With block, and using Worksheets instead of Sheets since sheets can refer to chart objects as well:
With Worksheets("InvPL")
If IsError(.Range("C59")) Then
Msgbox "C59 contains an error. If C57=0 then it is a divide by zero error."
Else
If .Range("C59").Value = "0" Then
.Rows("78:79").EntireRow.Hidden = True
Else
.Rows("78:79").EntireRow.Hidden = False
End If
End If
End With
You can change the logic a bit if you want to hide/un-hide those rows in the case of the error as well.
Another option would be to change the formula to return a zero instead of an error when C57 is 0:
=IF(C57=0,0,ROUNDUP(D59/C57,0))

Userform textbox fills in if Vlookup finds the information related to the numbers inserted

I have an userform where people have to fill in with data. If the data already exists, when they put the information in the DocumentTitleBox, other textboxes should automatically fill in.
My code works with letters, but not with numbers.
For example, when I put "aaa", it returns the vlookup values. But if I put "123", it won't do anything, even though there is a vlookup for it.
I cannot figure it out why. This is part of my code:
Private Sub DocumentTitleBox_Change()
On Error Resume Next
Result = WorksheetFunction.VLookup(DocumentTitleBox.Value, Worksheets("example").Range("D:E"), 2, False)
FIND = WorksheetFunction.VLookup(DocumentTitleBox.Value, Worksheets("example").Range("D:E"), 1, False)
On Error GoTo 0
If FIND = DocumentTitleBox.Value Then
NameBox.Value = Result
End If
Thank you in advance!
I always use this kind of thing. Could be cleaned up and stuff but I like the flexibility and I change stuff all the time so this works for me.
Private Sub DocumentTitleBox_Change()
If IsNumeric(DocumentTitleBox.Value) Then
ReturnRow = Application.IfError(Application.Match(DocumentTitleBox.Value + 0, Worksheets("example").Columns(4), 0), "Not Found")
Find = Application.IfError(Application.Index(Worksheets("example").Columns(5), ReturnRow), "Not Present")
Else
ReturnRow = Application.IfError(Application.Match(DocumentTitleBox.Value, Worksheets("example").Columns(4), 0), "Not Found")
Find = Application.IfError(Application.Index(Worksheets("example").Columns(5), ReturnRow), "Not Present")
End If
If Not Find Like "Not Present" Then
NameBox.Value = Find
Else
NameBox.Value = ""
End If
End Sub
PS: I donĀ“t know how to avoid the match functions odd behaviour with strings/numbers so I just go with the +0 and IsNumeric trick. One thing to note is case sensitivity, adjust that as needed, right now its not.
If DocumentTitleBox is a text box, try using DocumentTitleBox.Text instead of DocumentTitleBox.Value.

(Excel) Pick formula criteria/logical etc from another cell(reference)

Im writing an If+Or function and would like to use several cell references for the different Logicals in the function, instead of writing each logical statements in the original if+or function. Any ideas of how to solve this? Hope im not too unclear here..
As example: instead of writing =If(or(A1=A2,A3=A4),A1,0) I would like to write out all different logical values in a list of cells, and the just write the original if+or formula like this: =IF(OR(B1),A1,0) where B1 contains the text "A1=A2,A3=A4"
Thanks for any help on this!
You can use the INDIRECT function.
For example, if the value in cell A6 is 10, INDIRECT("A6") = 10.
So basically you can write INDIRECT("A6")=INDIRECT("A7") instead of the A1=A2 condition in your IF formula.
If you want to have "A1=A2" in one cell, you can use LEFT and RIGHT.
Here is an example: https://docs.google.com/spreadsheets/d/157tRicA55TFKKOi86yYBQScnjaQE6fYxaCHFdZx4uUM/edit?usp=sharing
PS: this solution is for Google Sheets so the solution might differ a little if you're using Excel but that should work for Excel too.
You CANNOT Have It All
Instead of using =IF(OR(B1),A1,0) you have to use
e.g. =IFOR(B1,A1) (I prefer "" instead of 0, sorry)
or =IFOR(B1,A1,0) if you (prefer 0 instead of ""),
or change the ElseValue in the declaration to 0,
then you can use =IFOR(B1,A1) to get 0.
Function IFOR(IfOrCell As Range, ThenCell As Range, _
Optional ElseValue As Variant = "", _
Optional SplitDelimiter As String = ",") As Variant
'Description:
'Pending...
'Recalculation
Application.Volatile
'Variables
Dim arrIfOr As Variant
Dim iIfOr As Integer
Dim blnIfOr As Boolean
'The Array: The Split
If InStr(IfOrCell.Value2, SplitDelimiter) = 0 Then Exit Function
arrIfOr = Split(IfOrCell.Value2, SplitDelimiter)
'An Additional Split Introducing the Boolean
For iIfOr = LBound(arrIfOr) To UBound(arrIfOr)
If InStr(arrIfOr(iIfOr), "=") <> 0 Then
If Range(Split(arrIfOr(iIfOr), "=")(0)).Value2 _
= Range(Split(arrIfOr(iIfOr), "=")(1)).Value2 Then
blnIfOr = True
Exit For
End If
End If
Next
'Output
If blnIfOr = True Then
IFOR = ThenCell.Value2
Else
IFOR = ElseValue
End If
End Function

Setting a Variable with and IF/OR Statement OR Calculation Results

I'm stuck on a bit of code. Essentially, I have a dynamic number of sheets where I'm trying to implement the same code. I can get the code to look at the correct sheet, but I'm struggling with the variant. The first line of code works, but only for 1 of the 2 values I'm looking for.
m = Application.Match(ws4.Range("D4").Value, .Columns("AW"), False)
To get the code to read D2 or D4, I've tried these lines of code, to no avail:
'm = Application.Match(ws4.Range("D4").Value, .Columns("AW"), False) Or Application.Match(ws4.Range("D2").Value, .Columns("AW"), False)
'm = ws.Range("AV").Value <> "0"
'm = ws.Range("AV").Value <> 0
'm = Application.Match(ws.Range("AV").Value) <> 0
'm = Application.Match(ws.Range("AV").Value <> 0)
'm = Offset(Application.Match(ws4.Range("D4").Value, .Columns("AW"), False), Application.Match(ws4.Range("D2").Value, .Columns("AW"), False))
When the actual code runs, this is what m is doing:
txt_Name = .Range("E" & m).Value
Also, this is tied to a post on another forum.
https://www.mrexcel.com/forum/excel-questions/1037378-convert-formula-vba-3.html
I think that if I can get this piece solved, I may be able to wrap up this project by end of day tomorrow, so I'm a little anxious.

Resources