I have in my userform listbox which display 10 columns. All rows have an individual ListIndex.
I would like to display f.e. in Msgbox value after select some row and click button "Show".
But this value is not avalible in listbox. So, I thought that all rows have individual ListIndex, I can use VLOOKUP for finding my target.
For example: I selected row no. 1 then ListIndex is 1 and this is my Look up Value. In my database sheet I have the same individual ID values.
Then of course, I have to declarate range, number of column and parameter "False".
Theoretically, I expect the result after that but it doesn't work.
My code:
Dim indexno As Long 'this is my delcaration for finding Look up Value
Dim myVLookupResult As Long 'this is my declaration for VLookup Result
indexno = ListBoxResult.ListIndex 'my Lookup Value is dynamic and depend of selected row
myVLookupResult = Application.VLookup(indexno, Worksheets("DataBase").Range("A1:J100"), 5, False)
MsgBox myVLookupResult 'should display result of VLOOKUP
But the result is error: Run-Time error: 13 - Type mismatch.
I guess the problem is with convert type of Dim from int to string.
Someone could support me, please? Thanks in advance!
The lookup value of a VLOOKUP worksheet function must be of the same data type as the data in which it is to be found. In your code the lookup value is of Long data type. If the column in which you are looking for it has text, the number you are looking for will not be found. So, you change the lookup value to Variant and hope that Excel will be able to work out what you want. But the better way is to examine your data column and look up the type of value you actually have there.
Next, given you are looking for a number which you have assigned to a Variant and Excel, in consequence, managed to find the string-equivalent of that number, that would be the functions return value, a text string. In most cases Excel is quite generous in this sort of situations but if it does complain about "Data Type" then it's because you are trying to assign a text string to a variable of Long data type.
Other than that, as #Michal Palko already pointed out, the ListBox's ListIndex is 0-based. If the numbers in your worksheet are 1-based the return of VLOOKUP won't be "totally different" but it will be from the adjacent row and therefore unexpected.
But I want to alert you to another possibility. As you know, you can load many columns into your list box. You can show some of the columns and hide others but you can access them all with code like this:-
With ListBox1
Debug.Print .List(.ListIndex, 3)
End With
This snippet will print out the value of the 3rd column in the row of ListIndex. You might also assign this value to a variable and, perhaps, have no need for VLOOKUP.
Related
I am using the XLookup function in Excel 365. I use a VBA script to create an ID number for a row of data and then I place that ID number into a cell.
If I use the XLookup function with Exact Match to locate that ID number and give me some value in a column next to it, I get an N/A.
However, if I manually locate that exact ID number and place the cell in edit mode and then exit the cell without taking any other step to alter the data in any way, the XLookup function returns the correct result.
If I leave the cell in it's initial state but use the Match Mode "-1" to get an exact match or next smaller, it returns the correct result. I don't want to leave it in that mode, because my final function will be two nested Xlookup functions that check one table for the ID and if not there, looks at an alternate table, so I don't want the first function to return a "next smaller" result.
When I generate the ID number in the VBA script, I generate it as the type "Variant" because the ID number is a decimal, such as 210.1. I then simply set that cell's Value to the variant ID number.
Here is a how I place the data into the cell:
Dim myID As Variant
Dim myCell As Variant
Dim myRange As Range
Set myRange = Range(someNamedRange)
myID = GenerateNextID 'This generates a variant in the form of something like 201.1
For Each myCell In myRange
myCell.Value = myID
myID = myID + 0.1
Next myCell
Excel seems to recognize it as a number; at least I seem to be able to do math functions on the ID number and get correct results.
I tried putting the lookup ID number in quotes to see if looking it up as text instead of a number produced a different result, but it didn't change my result.
Why am I getting this result? Is there a way to resolve it without using a different Match Mode?
You have a floating point precision error. The value in E11 is 211.39999999999998 rather than 211.4. Excel corrects the error when you re-enter the cell's value.
Write the value using a line like:
myID = Round(myID + 0.1, 1)
I am trying to store all the values of an excel column in an array.
set rangeDate to {value of range "A14:A100"}
repeat with date in rangeDate
if (date as string is equal to "01/01/2001") then
log "It works"
end if
end repeat
In my Excel I do have an exact date of 01/01/2001 formatted in the specified columns. When I remove the range and it is just cell A14 (where the date is) it works. But when I include the range A14:A100 it doesn't work.
I am new to applescript, I guess that it doesn't store the values as array values and instead a string object? Any help would be appreciated
You have 4 issues :
1) value of range should not be between {}, but between ()
2) 'Date' is a reserved word in Applescript, so you should not use it as the variable in the loop. I replaced it with 'myDate'.
3) instead of converting your date to string to compare with "01/01/2001", it is quicker to keep comparing 2 dates, and then, compare with the date "01/01/2001"
4) I think it is a bug (at least with my Excel version), but the rangeDate variable is not a list of dates as expected, but for me a list of list : {{01/02/01},{02/02/01},………} Therefore, each member of 'rangeDate' is not a date, but a list made on one item which is a date ! I am not sure, but it could also be that range definition could be a list of ranges... So I am using item 1 of sub list.
Anyway, script bellow is working :
tell application "Microsoft Excel"
activate
tell active sheet of document 1
set rangeDate to (value of range "A14:A100")
repeat with mydate in rangeDate
set TheDate to item 1 of mydate
if TheDate = (date "lundi 1 janvier 2001 00:00:00") then
log "It works"
end if
end repeat
end tell
end tell
Quickly getting the values of a range of cells is great news! But even better is that you can fill in the values of a range by defining the value of that range. This is SO MUCH FASTER than doing it one cell at a time.
When I tried getting the value of a column (a range of cells), I received a list of lists. Each item in the list had only one value - that is the value of the cell.
To speed up complex operations, once you've got the list of values, take the process out of the "tell Excel" block and let AppleScript do the calculations. Then turn the result back into a list of lists and define the value of the range in Excel.
I had a problem reading ranges with some cells containing #VALUE! (failed formulas). I didn't find a solution on the Internet, so I thought it would be a good idea to share my solution here. Comments & improvement are surely welcome. I'm inclined to think there is a more straightforward solution to the problem than this. :)
Getting all values with value of range can lead to a problem messing up the output of the script. AppleScript doesn't consider a cell's content "#VALUE!" (= missing values) a value since it is, well, missing. Therefore the script doesn't include the cell's content in the list of values. This obviously messes up the cell order in the values list, since it has less items than the actual range has cells. In this situation it is quite impossible to return each value to its original cell in the workbook. Adding ”of ranges” to the code includes all cells with missing values solving the problem.
N.B. The values will be displayed as a one-dimensional array. Handling multi-column ranges requires more work. Nonetheless the missing values are included.
set celVals to (value of ranges of range "A1:A4")
E.g. {2.2.2022, 1.1.2011, missing value, 3.3.2033}
In order to return the values back to the workbook it is required to build back the list of lists. A missing value will be written to its cell as an empty string. Of course the original (failed) formula can be written instead, if needed.
N.B. again. This code applies to one column situation only. A little more is needed to put back a multi-column range. I'm sure you'll manage. :D
set returningCelVals to {}
repeat with i from 1 to count of celVals
set end of returningCelVals to {item i of celVals}
end repeat
set value of range ("A1:A4") to returningCelVals
EDIT: I knew there is a better solution. Here it is:
set celVals to string value of range "A1:A4"
String value gives a two-dimensional array of values and error messages of the range. String value gives also e.g. cell's currency symbols, so it is perhaps not suitable to all situations.
I'm trying to develop an Excel application that asks our 4D database for information. To do that, I built a query builder and it works. Now I want to make it more generic so that when I call the query builder, I can pass it a range in which the tables and fields the query is based on are stored. Here is a line where I call the sub and pass it the parameters:
QueryDatabase Worksheets("TablesAndFields").Range("A2:R20"), Worksheets("TablesAndFields"), Worksheets("Import")
Here is the first line in the sub:
Sub QueryDatabase(QuerySpecs As Range, QuerySheet As Worksheet, TargetSheet As Worksheet)
One of the things I need to do is have the VBA figure out the row of the last fields in the various columns. Here is my current code:
LastRowReportTables = QuerySpecs.Offset(0, 3).End(xlDown).Row
LastRowQuery = QuerySpecs.Offset(0, 6).End(xlDown).Row
LastRowSort = QuerySpecs.Offset(0, 14).End(xlDown).Row
This returns the same value for all 3 of them (the second line of the range). It seems to do this regardless of which cells have values in them. For instance, in the case of the range specified above it will return 3. If the range is "A22:R40" it returns 23. What I really need is for it to return the row relative to it's position in the range, but I could fake that if necessary by subtracting the largest multiple of 20 less than the result. (I'm formatting my query builder to fit in 19 rows + a buffer row.) So far, I haven't even been able to get it to return different results for the LastRow variables.
In addition to the Offset method you see above, I've also tried putting it in a With QuerySpecs... End With block. I don't remember the exact result, but I couldn't get that to work either.
The next thing I will need to do is pull values out of the various cells kind of like this:
strStartCell = QuerySpecs.Offset(0, 18).Value
This throws a Run time error 13: Type mismatch. Does anybody have any advice on how to accomplish my goals? Thank-you!
I suggest reading the documentation of the resources you are trying to use.
This can be done by selecting the word which you want to search in the documentation, then press the [F1] key, that will take you to the Microsoft documentation page (i.e. if you select Row and press [F1] will take you to the page Range.Row Property (Excel).
Reading the documentation for Row and Offset will help you understand these Range.Properties and the returns you are getting from your code.
To get the last non-empty row in a range of one column, assuming all data in the range is continuous (i.e. there are no empty cells between rows with content), use these lines:
With WorksheetFunction
LastRowReportTables = .CountA(QuerySpecs.Columns(4))
LastRowQuery = .CountA(QuerySpecs.Columns(7))
LastRowSort = .CountA(QuerySpecs.Columns(15))
End With
Note: If the "QuerySpecs" range is a kind of database, all columns in the fields should have the same number of records, if so, it should be sufficient to obtain the last row for one field only.
To obtain the value of a cell within a range, use this line:
This returns the value of the field (column) 18,row 13 of the QuerySpecs range
strStartCell = QuerySpecs.Cells(13, 18).Value2
The Run-time error 13: Type mismatch is generated when trying to assign an array to a string variable.
I am using vlookup to populate an array in vba.
The array (InfArray) is an ~100 row by 3 column array. The first column of the array is already populated with integer values. These integer values correspond to a name. In one of my excel sheets I have a table that has each integer id listed with its corresponding name. So I want to use the vlookup function to check the id integer and populate the second column of the array with the actual name.
Here is the code statement I have to assign the second column (original bad code):
For y = 1 To UBound(InfArray,1)
Set InfArray(y,2) = Application.WorksheetFunction.VLookup(InfArray(y,1), NameSheet.Range("B2:C244"), 2, False).Value
Next y
It is giving me a Object variable or With block variable not set error.
When I cursor over the code text after running, I see that the
NameSheet.Range("B2:C244")
is where the object variable or with block variable not set error is located. This is really confusing. I put a
Debug.Print NameSheet.Name
In the code prior to the error and it is giving me the correct name of the excel sheet in question. So the code must be looking in the right worksheet, but somehow it is not getting the range...?
I'm lost, please help
EDIT: The error was a typo in the code
NameSheet.Range("B2:C244")
should have been
NamesSheet.Range("B2:C244")
Note the 's' at the end of NameS
It now works with the edits suggested
Working code line:
For y = 1 To UBound(InfArray,1)
InfArray(y,2) = Application.WorksheetFunction.VLookup(InfArray(y,1), NamesSheet.Range("B2:C244"), 2, False)
Next y
Apologies for the stoopid error.
Thanks for you help.
Get rid of the Set statement. It should only be used with object variables.
But also...
Piercing the boundary between VBA and Excel 100 times in a loop is not an efficient design. It would be much better to grab your table once, place it in a VBA array, and then just spin through this additional array to find the lookup values.
Remove the .Value from the VLOOKUP function. The Range.Value property is not part of VLOOKUP.
For y = 1 To UBound(InfArray,1)
InfArray(y,2) = Application.VLookup(InfArray(y,1), NameSheet.Range("B2:C244"), 2, False)
Next y
I've also reduced to down to Application.VLookup which is all that is necessary.
Trying to figure out a Macro that will do the following:
1) Find a specified header (ie: "status")
2) Filter this column to a specified value (ie: "discontinued")
3) Once filtered, find another specified column (ie: "replaced with")
The challenge for me is I can't figure out how to set it so the column "Field" is variable to the sheet its on.
I have multiple files that have the "status" header in different locations, (one file has it in column c, another in column f, etc). Is there a way to have excel find that specific column in each sheet is and filter it?
Find a specified header
You would implement this quite exactly as you'd subconsciously do in your head while "finding a specific header": iterate all columns from left to right until you find it, ...and throw a fit when it's not there. The function should be generic enough to not be attached to any specific worksheet, so you should pass it a worksheet as a parameter.
Something like this:
'returns column number of specified header, or -1 if not found.
Public Function FindHeader(sheet As Worksheet, header As String) As Integer
...
End Function
Find your header row, and loop through the non-empty cells. Your exit conditions would be either you've iterated all non-empty cells in the header row, or you've found a cell that matches the content you're looking for. When the loop exits, you need to know two things: whether the header was found, and if it was, under which column.
Filter this column for a specified value
You don't say how the value gets specified, so I'll assume all you need is a Sub that takes an Integer for the header column to filter, and a String parameter for the value to filter with. Using AutoFilter -related methods should get you there. Again you want to be able to use this code for any sheet, so pass one as a parameter:
Public Sub FilterForSpecificValue(sheet As Worksheet, columnIndex As Integer, filter As String)
...
End Sub
Find another specified column
Well, just reuse the function from #1, passing it the same sheet with another column header:
result = FindHeader(ActiveSheet, "AnotherHeader")
That's about as much as I can give you for the question you've asked; feel free to ask SO for any specific programming issues you might encounter implementing this. However it's possible you won't even need to ask, because it's very likely that somebody else somewhere has documented exactly how to do it - here are a couple links that can help you:
Selecting an entire row of data (StackOverflow)
Looping Through a Range of cells (MSDN)
StringContainsAny (StackOverflow)
AutoFilter Method (MSDN)