Excel vba: check if VLookup returns empty value, given that Vlookup substitutes empty with zero - vba

So I have a table for different regions and corresponding values for different years 2014-2017. Not all regions have value for the year 2017. However, if I make a WorksheetFunction.VLookup(...) it will return 0 if the cell in question is empty.
I need to put my special value (-1) instead of 0 if the cell for a given region is empty, however, I can't differentiate between real zeroes and zeroes Vlookup returns instead of empty values. How would I do it?
EDIT: I can't use IsEmpty on individual cells, because I don't know the result's cell's address - I loop though them in a cycle without even knowing a first argument's address. All I have is the result - either a real or a fake zero.

You can check if VLOOKUP returns an empty cell.
As a worksheet formula you can use: =IF(VLOOKUP(K9,$F$2:$G$6,2,FALSE)="",-1,VLOOKUP(K9,$F$2:$G$6,2,FALSE))
As a VBA example you could use:
Public Function MyVlookup(lookup_value, table_array As Range, col_index_num As Long, Optional range_lookup As Boolean = False) As Variant
Dim ReturnValue As Variant
ReturnValue = WorksheetFunction.VLookup(lookup_value, table_array, col_index_num, range_lookup)
If IsEmpty(ReturnValue) Then
MyVlookup = -1
Else
MyVlookup = ReturnValue
End If
End Function

Worked this answer
=if(len(vlookup(a2,f$2:g$20,2))=0,"",vlookup(a2,f$2:g$20,2))
The trick is to use any checking functions not on result = VLookup(...) but rather on VLookup itself, before it had time to change emptiness to zero.

I am not sure but I think using IF, ISEmpty will solve your problem.
Use WorksheetFunction.Match to get cell address
currentCell= the cell in which you'll be putting value
concernedCell=The cell which can be empty
If(Isempty(concernedCell)) Then
currentCell.value=-1
Else
currentCell.value=concernedCell.value

Related

Finding and return values with associated rows on excel on multiple pages

Thanks in advance for any help! I haven't used much VBA in excel and can't work out how to do what I need, however I believe I need it to achieve the function I need within the workbook.
I have 31 data pages, in which I need to find if certain information is present and display it on a summary page.
I need is to check if there are values in the column AQ, If there is then I need the data returned in that row in columns E, F and G.
There could be multiple instances per sheet or none per sheet.
Hopefully this will example explain it better:
E F G ... AQ
Date Name Location Exception
2 1-12-17 Dave England
3 1-12-17 Sarah Wales Exp
In the example data above the information I would want returned on the Summary page is from row 3. (This type of data is on each of the 31 other pages)
Hope this makes sense! Any help on how to do this would be greatly appreciated.
There are a number of different ways you could tackle this problem, for example, pivot tables with specific filter conditions, a UDF that finds the matches and prints them to the output you'd like, etc. In general, it's not a bad idea to use the Range.Find method and loop through all the matches.
This requires a certain amount of programming time and energy, which not everyone has, although most people who use Excel a lot eventually end up using vLookup a lot. I've always been unsatisfied with vLookup, it's so limited compared to the vba Range.Find method. Just for you, since it's almost Christmas and I ran out of work that I'm actually paid to do, here's a little gem that should help solve your problem.
It's a UDF lookup that allows you specify which number match to return, and return a custom offset in rows or column to retrieve as a value. Incrementing the variable matchNum will give you all the matches in the range, and you can return whatever columns you want using the appropriate amount of offset.
The use of the Range.Find method should give you an idea of how you could use code to populate a worksheet with exactly what you wanted without using a UDF lookup function. I'll leave that as an exercise for the reader.
'################################################################################################################################
' findwhat: the value you want to find. (needle)
' where: the range you want to look for findwhat (haystack)
' matchNum: if the needle is found more than once in the haystack, find the nth target.
' rowoffset: offset your return value from the target, positive will return the value of the cell below the target.
' columoffset: offset your return value from the target, positive will return the value of the cell to the right of the target.
'################################################################################################################################
Public Function powerLookup(findwhat As Variant, where As Range, Optional matchNum As Long = 1, Optional rowOffset As Long = 0, Optional columnOffset As Long = 0) As Variant
Dim rngResult As Range, firstAddress As String
Set rngResult = Nothing
On Error GoTo Errorhandler ' if something breaks return #NA (will definitely happen if first find call fails)
Do While matchNum > 0 'loop through the matches until there are no matches or we've reached the target matchnumber
'the first time, rngResult will be Nothing, so the first find can't have rngResult as an input.
With where
If rngResult Is Nothing Then
Set rngResult = .find(findwhat, , xlValues)
firstAddress = rngResult.Address 'keep this to know if we've looped past the start
Else
'if rngResult is not nothing we've already found a match, find the next match until matchnum is 0
Set rngResult = .find(findwhat, rngResult, xlValues)
If rngResult.Address = firstAddress Then
'if we reach the first address we've looped around, no more matches found, throw #NA error
powerLookup = CVErr(xlErrNA)
Exit Function
End If
End If
End With
matchNum = matchNum - 1
Loop
powerLookup = rngResult.offset(rowOffset, columnOffset).Value 'offset the output
Exit Function
Errorhandler:
powerLookup = CVErr(xlErrNA)
End Function

What am I missing in this IsEmpty clause?

In an Excel VBA module I'm building, I have some code to execute if a table of out-of-gauge cargo contains anything. I initially wrote this:
If Not IsEmpty(Range("OOGData")) Then
...
Else
...
End If
But even when OOGData is empty, it keeps returning False. I've tried it with If IsEmpty(Range("OOGData")) = False Then` but that doesn't seem to make any difference. My current code is
If IsEmpty(Range("OOGData")) = False Then
...but that still activates with the empty range.
I've made sure there are no formulae, hidden values or anything that could be showing up.
Any idea what the problem could be?
According to this information:
Returns a Boolean value indicating whether a variable has been
initialized.
In your code you are not working with variable therefore you shouldn't expect correct value.
When checking single cell you should use Len() function instead:
If Len(Range("OOGData"))=0 Then
'cell is empty
When checking if range of cells is empty use this solution:
If WorksheetFunction.CountA(Range("OOGData"))=0 Then
'rabge is empty
The final alternative I can think of is to use loops.
I've decided to cheat. Instead of checking the list there, I've added a Boolean variable (bContainsOOG) that's set to True any time an OOG cargo item is added to the OOG list, and then the reporting sub checks against that instead. But thanks both of you for those suggestions, they'll come in handy in another bit I was getting stuck on. :-)
This is a function that I thinks very closely fits:-
' Function to determine the last (first blank/null/empty) cell in a series of data in either a column
' or row
' Usage :- DataSeriesEnd(w,r,b)
' where w is the name of the worksheet.
' r is the row or column as an integer
' b is a boolean (true/false) if true then processing a column, if false a row
Public Function DataSeriesEnd(Worksheet As String, rc_index As Integer, bycolumn As Boolean) As Integer
Dim cv As Variant
For DataSeriesEnd = 1 To 500
If bycolumn Then
cv = Worksheets(Worksheet).Cells(DataSeriesEnd, rc_index)
Else
cv = Worksheets(Worksheet).Cells(rc_index, DataSeriesEnd)
End If
If IsNull(cv) Or IsEmpty(cv) Or cv = "" Then Exit For
Next DataSeriesEnd
' Position to previous cell (i.e. this one is empty)
DataSeriesEnd = DataSeriesEnd - 1
End Function
Note! There is a limit of 500 rows/columns

#VALUE error with Excel VBA Function

In my Excel spreadsheet I have two columns.
A contains strings with the values 'Yes', 'No' or 'Maybe'.
B contains strings with a year in.
I need a function to determine the number of occurrences of a year in column B, where the equivalent value in column A is 'Yes'.
I currently have the following code:
Function CountIfYearAndValue(Rng As Range, YNM As String, Year As String) As Integer
Dim count As Integer
count = 0
For Each c In Rng.Cells
If (StrComp(Abs(c.Value), Year, vbTextCompare) = 0) And (StrComp(Cells(c.Row, A), YMN, vbTextCompare) = 0) Then count = count + 1
Next
CountIfYearAndValue = count
End Function
The idea of this code is that we iterate through every cell in the range given (a range on column B) and check if the year is equal to the Year parameter. And if the equivalent cell on column A is equal to the YNM parameter we increment the count variable.
For some reason this code does not work when I use the following parameter:
=CountIfYearAndValue('Years'!B1:B7,"Yes","Year 7")
It just does the #VALUE error and refuses to display any outcome.
Any help would be much appreciated.
Edit: All of the values in both cells are on of an unformatted datatype ('General') and no cells are blank.
It sounds like you are reinventing the wheel... There already is a built in function (advantage: being much faster than a UDF) that does exactly what you are after. It is called COUNTIFS()
All YESes for Year 7 in rows 1 to 10.
=COUNTIFS(B1:B10, "Year 7",A1:A10, "Yes")
I just had a quick look at your code and I think there are possibly a few reasons why your original code is not working as expected.
YNM is a valid column name therefore it should not be used as a variable name. You should avoid naming your variables like that - give it a more meaningful name
YNM != YMN as you had it in your code (see function definition and then the misspelled version in the StrComp() function)
Year is a valid VBA built in function, therefore once again you should avoid using it as a variable name as you're exposing yourself to a naming collision.
Add Option Explicit at the top of your module. This requires you to Dimension all you variables. It's always recommended for many many reasons.
rng variable is of Range type therefore you do not need to explicitly add the .Cells property to it. Even though it may help in some cases - at a bit more advanced level you may face some runtime type compatibility issues. ( runtime may convert your rng Range variable to a 2D array etc )
Added an explicit conversion in the second StrComp() function around the c.Offset(0, -1) as you don't want the runtime to (rare but still possible) convert your Yes to a Boolean data type. Explicit conversion to a String just gives you that extra protection ;p (lol)
therefore, something like this returns the correct value
Function CountIfYearAndValue(rng As Range, choice As String, myYear As String) As Long
Dim count As Long
count = 0
Dim c As Range
For Each c In rng
If (StrComp(c, myYear, vbTextCompare) = 0) And (StrComp(CStr(c.Offset(0, -1)), choice, vbTextCompare) = 0) Then
count = count + 1
End If
Next c
CountIfYearAndValue = count
End Function
Right, I hope this helps you understand bits and pieces :) any questions please leave a comment

Excel SUMPRODUCT with VBA based conditions

Does anyone know how to use a VBA function within a worksheet based call to SUMPRODUCT?
This works fine, summing the values in column N where column L contains "Y" and col A contains a different value to col K...
=SUMPRODUCT(--(Input!L1:L100="Y"), --(Input!A1:A100<>Input!K1:K100), Input!N1:N100)
But I want to be able to apply more logic than just A<>K in my second criteria, something like this...
=SUMPRODUCT(--(Input!L1:L100="Y"), --(MatchNames(Input!A1:A100,Input!K1:K100)), Input!N1:N100)
I have a function called MatchNames in my VBA, but I can't work out what it needs to return in order to work. I've tried returning an array of boolean, integer (set to 0 or 1), I've tried transposing the results before returning them, but nothing is working. I've debugged through the MatchNames function, and it does return something "useful" (i.e. an array of booleans), so it's not that the function is bombing out part way through, but I get #VALUE! when I try to use it in a SUMPRODUCT.
This is my VBA function...
Public Function MatchNames(ByVal rng1 As Range, rng2 As Range) As Boolean()
Dim blnOut() As Boolean
Dim k As Long
ReDim blnOut(rng1.Rows.Count - 1)
For k = 1 To rng1.Rows.Count
If rng1.Cells(k, 1).Value <> "" Then
If rng1.Cells(k, 1).Value <> rng2.Cells(k, 1).Value Then
blnOut(k - 1) = True
End If
End If
Next
MatchNames = blnOut
End Function
I think your MatchNames array needs to be transposed as you suggest (because it appears to be returning the equivalent of a row of values - which doesn't work with the other columns of values in SUMPRODUCT).
I don't know how you'd transpose that in VBA but if you can't do that then transpose in SUMPRODUCT like
=SUMPRODUCT(--(input!L1:L100="Y"),--TRANSPOSE(MatchNames(input!A1:A100,input!K1:K100)), input!N1:N100)
but use of TRANSPOSE means that formula now needs to be "array-entered" with CTRL+SHIFT+ENTER
...or you can use MMULT which will multiply a 1x100 range by a 100x1, i.e.
=MMULT(MatchNames(input!A1:A100,input!K1:K100)+0,input!N1:N100*(input!L1:L100="Y"))
for that latter formula to work the sum range - input!N1:N100 - must be all numeric

VBA Count cells in column containing specified value

I need to write a macro that searches a specified column and counts all the cells that contain a specified string, such as "19/12/11" or "Green" then associate this number with a variable,
Does anyone have any ideas?
Do you mean you want to use a formula in VBA? Something like:
Dim iVal As Integer
iVal = Application.WorksheetFunction.COUNTIF(Range("A1:A10"),"Green")
should work.
This isn't exactly what you are looking for but here is how I've approached this problem in the past;
You can enter a formula like;
=COUNTIF(A1:A10,"Green")
...into a cell. This will count the Number of cells between A1 and A10 that contain the text "Green". You can then select this cell value in a VBA Macro and assign it to a variable as normal.
one way;
var = count("find me", Range("A1:A100"))
function count(find as string, lookin as range) As Long
dim cell As Range
for each cell in lookin
if (cell.Value = find) then count = count + 1 '//case sens
next
end function
If you're looking to match non-blank values or empty cells and having difficulty with wildcard character, I found the solution below from here.
Dim n as Integer
n = Worksheets("Sheet1").Range("A:A").Cells.SpecialCells(xlCellTypeConstants).Count
Not what you asked but may be useful nevertheless.
Of course you can do the same thing with matrix formulas.
Just read the result of the cell that contains:
Cell A1="Text to search"
Cells A2:C20=Range to search for
=COUNT(SEARCH(A1;A2:C20;1))
Remember that entering matrix formulas needs CTRL+SHIFT+ENTER, not just ENTER.
After, it should look like :
{=COUNT(SEARCH(A1;A2:C20;1))}