VLookUp not working - Property could not be assigned - vba

My problem is, that when using the VlookUp I do get the Error:
The VLookup-Property of the WorksheetFunction-Object could not be assigned
' LookUp missing Data
Dim lookupRange As Range
Set lookupRange = Sheets("Kundenlisten HLK 2018").Range("A1:Y10354")
' Fill in Companyname
Dim tmp As String
tmp = Values(0)
tmp = TrueTrim(tmp)
testing.Cells(8, counter) = Application.WorksheetFunction.VLookup(tmp, lookupWS.Range("A2:Y10354"), 2, False)
Values = None
counter = counter + 1
lookupWS is the Name of the Worksheet
As you can see the table I am trying to lookup is filled with values from A to Y. The first column is the key I am trying to look up, but then the error from above occurs. The tmp variable is a String with a unique ID to look up the missing values, while the "2" is supposed to be the company name in the second column of the Range.
I looked up on the docs, but the types and everything are fine, I even checked while debugging.
testing.Cells(8, counter) can't be the source of the problem aswell, since I am using it before like this
testing.Cells(28, counter) = Left(mail.ReceivedTime, 10)
and it works

It's difficult to know what the problem is without any data, but here's something to help you in the right direction.
It's better to use Find and Offset than
WorksheetFunction.Vlookup in VBA
Something like this gives you exactly the same result, but you have much more control:
Sub Test()
Dim valueFound As Range
Set valueFound = lookupWS.Range("A2:A10354").Find(What:="Something", lookat:=xlWhole) 'xlWhole is equivalent to FALSE in VLOOKUP
If valueFound Is Nothing Then
MsgBox "Nothing found"
Else
MsgBox valueFound.Offset(0, 1) 'offsetting by 1 is equivalent to using 2 in a VLOOKUP
End If
End Sub

Related

Knowing the assigned name of a cell instead of the "A1" name

Context:
I have several lists in my sheet (1 column wide, 1-10 rows long). When I right click a cell in these lists, I can do several options, that all work well. I have given a name to the cell at the top of each of these lists (ex. Cell A1 has been given the name cell_1, B10 is names cell_2, etc).
I would like to know if the cell I am right clicking on is the one at the top of the list; is it named "cell_(number)"? If it is not, it checks the cell on top of that one. Does it have a name that starts with "cell_"? If not, check the one on top, etc. Until I can figure out the user clicked on an element of WHICH list.
TL;DR The actual question
I can use ActiveCell.Address, which gives me something like "A1" whether or not I have assigned a name to that cell. ActiveCell.Name gives "Sheet1!A1", so it's not much better. Any idea how to get it to return the name I have assigned instead?
Create a UDF to test the application names, it's less efficient but contains error handling within the function itself:
Sub SO()
'// Example how to call function
Debug.Print GetCellName(Range("A1"))
End Sub
Function GetCellName(myCell As Excel.Range) As Variant
Dim nameCheck As Variant
For Each nameCheck In Application.Names
If Replace(Replace(Replace(nameCheck, "=", ""), "'", ""), "!", "") = _
CStr(myCell.Parent.Name & myCell.Address) Then
GetCellName = CStr(nameCheck.Name)
Exit Function
End If
Next
GetCellName = CVErr(Excel.xlErrName)
End Function
Note you can also use this function in a worksheet cell like so:
=GetCellName(A1)
Perhaps this would work. This function returns the names assigned to a cell (or bigger range for that matter). If there's more than one name, it returns it as an array for array formula...or the user can supply an index to return only the desired name position
Public Function CellIsInRangeNames(sheetname As String, checkRange As Range, Optional itemNumber As Variant) As Variant
Dim oNM As Name
Dim oSht As Worksheet
Dim isect As Range
Dim namesCollection() As Variant
Set oSht = Worksheets(sheetname)
Dim i As Integer
i = -1
For Each oNM In oSht.Names
Set isect = Application.Intersect(Range(oNM.Name), checkRange)
If Not isect Is Nothing Then
i = i + 1
ReDim Preserve namesCollection(0 To i)
namesCollection(i) = CStr(oNM.Name)
End If
Next oNM
If i = -1 Then
'didn't find any
CellIsInRangeNames = xlErrName
ElseIf Not IsMissing(itemNumber) Then
'user wanted this instance only
If (itemNumber - 1 > UBound(namesCollection)) Or (itemNumber - 1 < LBound(namesCollection)) Then
CellIsInRangeNames = xlErrValue
Else
CellIsInRangeNames = namesCollection(itemNumber - 1)
End If
Else 'here's the list as an array
CellIsInRangeNames = namesCollection
End If
End Function

VBA Range is Nothing

Running the code below and what I'm hoping to see as a result is to have all columns with 0s on row 27 hidden - but depending on the frequency of the data, the range for those columns to be hidden is different. Basically anything that is in a sheet that starts with Daily/monthly/weekly will have to have columns hidden, all the rest of the sheets should be left alone.
It worked on a simple workbook using an if statement (sheets starting with X vs. all other), but when I added the case statement it broke...
The line marked down with bold is the one where I get an error:
Run-time error '1004'
Application-defined or object-defined error
I'm new to this, so please feel free to suggest a good vba tutorials website/book.
Sub Hide_Zero_Columns()
Dim WS As Worksheet
Dim Col_to_hide As Range
Dim Range_to_hide As Range
Dim X As Integer
For Each WS In ThisWorkbook.Sheets
Worksheets(WS.Name).Activate
With WS
Select Case Data_Frequency_Sheets
Case Left(WS.Name, 5) = "Daily"
Set Range_to_hide = Range("BDV$27:CWH$27")
Case Left(WS.Name, 5) = "Month"
Set Range_to_hide = Range("AY$27:CO$27")
Case Left(WS.Name, 5) = "Weekl"
Set Range_to_hide = Range("HF$27:NN$27")
Case Else
Set Range_to_hide = Range("A1:B1")
End Select
Select Case Data_Condition
Case Left(WS.Name, 5) = "Daily"
X = 1
Case Left(WS.Name, 5) = "Month"
X = 30
Case Left(WS.Name, 5) = "Weekl"
X = 7
Case Else
X = 999
End Select
If X <> 999 Then
For Each Col_to_hide In ActiveSheet.Range(Range_to_hide) '<-- Error here
If UCase(Col_to_hide) = 0 Then
Col_to_hide.EntireColumn.Hidden = True
Else: Col_to_hide.EntireColumn.Hidden = False
End If
Next Col_to_hide
End If
End With
Next
ActiveWorkbook.Worksheets("Registrations").Activate
End Sub
Since you have already defined a Range, you the problem is you are trying to evaluate: Sheet.Range(Range) which throws the error you are getting.
Since it appears you are wanting to iterate across the columns, all you need to do is change the line to this:
' Iterate across the columns in the defined range.
For Each Col_to_hide In Range_to_hide.Columns
' Each "Col_to_hide" will represent all cells within the column.
MsgBox Col_to_hide.Address
The error you're getting is because you're passing a Range object as the argument to Activesheet.Range() and it doesn't accept that because of the Range object's default value. It would be like doing this in the Immediate Window
?Range(Range("A1")).Address
You'll get the same error. Whereas with
?Range("A1").Address
You don't. You could do this too
?Range(Range("A1").Address).Address
So the thing is that when you don't specify a property for an object, like Range("A1") instead of Range("A1").Address, then the default property is used. The Range object is a bit strange, but in this case its default property is the Value property. So Activesheet.Range(Range_to_hide) is the same as Activesheet.Range(Range_to_hide.Value). And if Range_to_hide is a multi-cell range, then it's Value property returns an array, which you definitely can't pass into a Range's argument.
That's the explanation, the short answer is simply to use Range_to_hide, like
For Each Col_to_hide in Range_to_hide.Columns
Why the .Columns. Remember when I said that the Range object was a little strange. Well, unlike most objects, it has two default properties based on the context you're using it in. Earlier, the default property was Value, but in the context of a For..Each, the default value is Cells. If you don't specify .Columns in your For..Each, then it will loop through every cell in the Range. In fact, I always specify how the range is aggregated, even if it's the default Cells.

Range object returns empty values

I have a set range to a variable in this fashion:
Dim srcRng As Range
Set srcRng = Range(hrwb.Worksheets(1).Range(yomColAddress)(1).Address, _
Cells(hrwb.Worksheets(1).Range(yomColAddress).row + 200, rightMostCol)(1).Address)
for some weird reason when I call
srcRng(1) 'actually instead of 1 is i that runs 1 to srcRng.Count
it doesn't return the upper leftmost cell value. Any ideas why?
(for those who are not familiar with this technique: http://www.cpearson.com/excel/cells.htm)
Informations:
at execution time the variables yomColAddress=$AL$9 and righMostCol=40
hrwb.Worksheets(1).Range(yomColAddress)(1) works as expected.
With MsgBox srcRng(1).Address & " value:" & srcRng(1).Value I get "$AL$9 value:"
The value of AL9 is the text "yom"
The actual code is:
Dim srcRng As Range
Set srcRng = Range(hrwb.Worksheets(1).Range(yomColAddress)(1).Address, Cells(hrwb.Worksheets(1).Range(yomColAddress).row + 200, rightMostCol)(1).Address)
Dim i As Integer
i = 1
While (weekDayCol = 0 And i <= srcRng.count)
If loneHebDayLetter("à", "ä", srcRng(i)) Then'loneHebDayLetter checks some conditions on a cell
weekDayCol = srcRng(i).Column
End If
i = i + 1
Wend
I think I get what goes wrong here:
The code itself is working well but not on the good data (This is a supposition but I just did some tests with a custom workbook)
Short version
Just add srcRng.Select after Set srcRng (no real interest but to understand what it does) and I think you will get what happens if my supposition is correct.
Longer version
When you do Set srcRng = ... it does create the correct Range but it is not linked to any sheet actually ... It just means remember a Range which goes from cell X to cell Y.
The point is: The sheet (let's say "sheet2") where your code is executed isn't the same as the one where the datas are (say "sheet1") so srcRng(1) is understood as Sheets("sheet2").srcRng(1) instead of Sheets("sheet1").srcRng(1) (<- that's what you want)
Even if not elegant, this should work:
Dim srcRng As Range
With hrwb.Worksheets(1)
Set srcRng = Range(.Range(yomColAddress)(1).Address, Cells(.Range(yomColAddress).row + 200, rightMostCol)(1).Address)
Dim i As Integer
i = 1
While (weekDayCol = 0 And i <= srcRng.count)
If loneHebDayLetter("à", "ä", .Range(srcRng.Address)(i).Value) Then 'I assume it take the value not the cell: if it take the cell you may get an error!
weekDayCol = srcRng(i).Column
End If
i = i + 1
Wend
End With
What is important is the use of .Range(srcRng.Address)(i).Value to access the value in the right worksheet! (That's why this trick is not needed here: srcRng(i).Column because colum numbers do not change from one sheet to an other)
(NOTE: I used with to optimize/clarify the code)
If something isn't clear tell me

VBA Range.value function causing unexpected end

This must seem like a terribly simple question, but I cannot figure out why my functions are ending unexpectedly on the Range.value = val call. Perhaps I am missing something very basic, but I have tested these out and each one of them are failing to resolve to anything and I don't know how to capture the error.
Here is the initial function:
Function incrementCount(upper As Range, Summed As Range, ParamArray sums() As Variant)
Dim deduct As Integer
Dim summation As Integer
Dim elem As Variant
Dim i As Long
Dim temp As Range
up = upper.Value
summation = Summed.Value
'Initialize the starting points of the increments
For i = LBound(sums) To UBound(sums)
MsgBox IsObject(sums(i)) 'Prints out as an true
MsgBox TypeName(sums(i)) 'Prints out as Rnage
MsgBox sums(i).Value 'Prints out as 0
Set temp = sums(i)
MsgBox temp.Value 'Prints out as 0
Set temp = Summed
MsgBox temp.Value 'Prints out as 1 (which is correct)
temp.value = 3 'Errors here
MsgBox temp.Value 'Never makes it to this line
sums(i).Value = 1 'I have also tried this with the same result
Next i
<more code that is never reached>
End Function
I am at my wits end. I have searched MSDN, stackoverflow, and all the many excel forums and all of them show setting values to a range like this. I have even separated the setting of a range value to a different function like this:
Function testsub(thecell As Range, thevalue As Integer)
thecell.value = thevalue
End Function
Ultimately i would like to be able to do something like discussed in this article where I loop over a random assortment of ranges and will increment them. Any help at all would be greatly appreciated.
You have not specified how IncrementCount() is being called.
If your function is being called from a worksheet cell, then it is "bombing out" at the correct line. A UDF called from a cell cannot modify the contents of other cells, it can only return a value.

pulling out data from a colums in Excel

I have the following Data in Excel.
CHM0123456 SRM0123:01
CHM0123456 SRM0123:02
CHM0123456 SRM0256:12
CHM0123456 SRM0123:03
CHM0123457 SRM0789:01
CHM0123457 SRM0789:02
CHM0123457 SRM0789:03
CHM0123457 SRM0789:04
What I need to do is pull out all the relevent SRM numbers that relate to a single CHM ref. now I have a formular that will do some thing like this
=INDEX($C$2:$C$6, SMALL(IF($B$8=$B$2:$B$6, ROW($B$2:$B$6)-MIN(ROW($B$2:$B$6))+1, ""), ROW(A1)))
however this is a bit untidy and I really want to produce this same using short vb script, do i jsut have to right a loop that will run though and check each row in turn.
For x = 1 to 6555
if Ax = Chm123456
string = string + Bx
else
next
which should give me a final string of
SRM0123:01,SRM123:02,SRM0256:12,SRM0123:03
to use with how i want.
Or is ther a neater way to do this ?
Cheers
Aaron
my current code
For x = 2 To 6555
If Cells(x, 1).Value = "CHM0123456" Then
outstring = outstring + vbCr + Cells(x, 2).Value
End If
Next
MsgBox (outstring)
End Function
I'm not sure what your definition of 'neat' is, but here is a VBA function that I consider very neat and also flexible and it's lightning fast (10k+ entires with no lag). You pass it the CHM you want to look for, then the range to look in. You can pass a third optional paramater to set how each entry is seperated. So in your case you could write (assuming your list is :
=ListUnique(B2, B2:B6555)
You can also use Char(10) as the third parameter to seperat by line breaks, etc.
Function ListUnique(ByVal search_text As String, _
ByVal cell_range As range, _
Optional seperator As String = ", ") As String
Application.ScreenUpdating = False
Dim result As String
Dim i as Long
Dim cell As range
Dim keys As Variant
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
On Error Resume Next
For Each cell In cell_range
If cell.Value = search_text Then
dict.Add cell.Offset(, 1).Value, 1
End If
Next
keys = dict.keys
For i = 0 To UBound(keys)
result = result & (seperator & keys(i))
Next
If Len(result) <> 0 Then
result = Right$(result, (Len(result) - Len(seperator)))
End If
ListUnique = result
Application.ScreenUpdating = True
End Function
How it works: It simple loops through your range looking for the search_string you give it. If it finds it, it adds it to a dictionary object (which will eliminate all dupes). You dump the results in an array then create a string out of them. Technically you can just pass it "B:B" as the search array if you aren't sure where the end of the column is and this function will still work just fine (1/5th of a second for scanning every cell in column B with 1000 unique hits returned).
Another solution would be to do an advancedfilter for Chm123456 and then you could copy those to another range. If you get them in a string array you can use the built-in excel function Join(saString, ",") (only works with string arrays).
Not actual code for you but it points you in a possible direction that can be helpful.
OK, this might be pretty fast for a ton of data. Grabbing the data for each cell takes a ton of time, it is better to grab it all at once. The the unique to paste and then grab the data using
vData=rUnique
where vData is a variant and rUnique is the is the copied cells. This might actually be faster than grabbing each data point point by point (excel internally can copy and paste extremely fast). Another option would be to grab the unique data without having the copy and past happen, here's how:
dim i as long
dim runique as range, reach as range
dim sData as string
dim vdata as variant
set runique=advancedfilter(...) 'Filter in place
set runique=runique.specialcells(xlCellTypeVisible)
for each reach in runique.areas
vdata=reach
for i=lbound(vdata) to ubound(vdata)
sdata=sdata & vdata(i,1)
next l
next reach
Personally, I would prefer the internal copy paste then you could go through each sheet and then grab the data at the very end (this would be pretty fast, faster than looping through each cell). So going through each sheet.
dim wks as worksheet
for each wks in Activeworkbook.Worksheets
if wks.name <> "CopiedToWorksheet" then
advancedfilter(...) 'Copy to bottom of list, so you'll need code for that
end if
next wks
vdata=activeworkbook.sheets("CopiedToWorksheet").usedrange
sData=vdata(1,1)
for i=lbound(vdata) + 1 to ubound(vdata)
sData=sData & ","
next i
The above code should be blazing fast. I don't think you can use Join on a variant, but you could always attempt it, that would make it even faster. You could also try application.worksheetfunctions.contat (or whatever the contatenate function is) to combine the results and then just grab the final result.
On Error Resume Next
wks.ShowAllData
On Error GoTo 0
wks.UsedRange.Rows.Hidden = False
wks.UsedRange.Columns.Hidden = False
rFilterLocation.ClearContents