Excel VBA: Using 2 variables with Match - excel

ws2.Range("D3").Value = Application.WorksheetFunction.Match(variable_i, ws2.Range("A:A"), 0)
ws1 = worksheet 1
ws2 = worksheet 2
On ws1 I have 2 columns of drop down boxes (data validation list)
the selection from the first column of drop down boxes is assigned to the variable "variable_i"
the selection from the second column of drop down boxes is assigned to the variable "variable_p"
It pulls its data from ws2 (the range is all of column a)
It is looking for the selection from the drop down box with the variable "variable_i"... the code works fine, it returns the number row on D3 without any issues
I'm looking for it to return the row number that contains both variables in the 2 columns (variable_i and variable_p)
I've tried the following code:
ws2.Range("D3").Value = Application.WorksheetFunction.Match(variable_i & variable_p, ws2.Range("A:B"), 0)
as I had seen that online, but it doesn't work for me.
The error I get is:
"Run-time error '1004':
Method 'Match' of object 'WorksheetFunction' failed
What would be the best way to have multiple variables be the lookup?
Any help would be much appreciated!

The key for you I believe could be to use EVALUATE() function. Multiple criteria would mean you most likely need an array which EVALUATE will recognize. Also concatenating values and ranges to check for two criteria is definately not the best solution. Imagine cell A1=1 and B1=101. What happens if you concatenate ranges to 1101 and you look for criteria1 10 and 11 instead of 1 and 101?
A simple evaluate example:
Debug.Print Evaluate("MATCH(1,(A1:A6=1)*(B1:B6=""B""),0)")
In my example I have one Long and one String variable, respectively 1 and B. Would I use your named variable variable_i and variable_p I could make a code like:
ws2.Range("D3") = Evaluate("MATCH(1,('Sheet1'!A1:A6=" & variable_i & ")*('Sheet1'!B1:B6=""" & variable_p & """),0)")
Notice the difference of adding a Long or a String variable? That's because to evaluate correctly in VBA there needs to be two quotation marks surrounding a String variable. A number can do without.
Also notice I specified sheet names in the code, you can change that according to your needs

Related

Why is my VBA with relative references returning incorrect references?

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.

How to use a variable WS name that references another WS and a variable range, with a MIN function

I have a WS named Stats and I want to fill a column with the MINIMUMs. The data is in the same WB, but on another WS called Data.
The data is in rows, so MIN calc would be performed on range B to IQ columns.
And I need to calc the MIN for rows 14 to 1868.
The following code works, but name and range of the data is hard coded:
Worksheets("Stats").Range("B14:B1868").Formula = "=MIN(Data!B58:IQ58)"
So my problem is that every workbook has a different worksheet name for the data. My macro has to work for all WBs.
I've tried the Indirect function, and this configuration only works for one row of data and populates the rest of the column with the same number(note that $A$2 is the location with the Worksheet name that contains the data):
Worksheets("Stats").Range("B14:B1868").Formula = "=MIN(Indirect($A$2&""!B58:IQ58"")"
I have tried so many different configs and can't figure this one out. I'll get a name or a ref error...it's driving me nuts, hoping someone here can help me!
Thanks in advance:-)
You can use a cell reference's value just as easily as a string literal.
(Sense of deja-vu there - I wrote that as part of an answer just this morning!)
So, instead of hard-coding "Data" into the formula
Worksheets("Stats").Range("B14:B1868").Formula = "=MIN(Data!B58:IQ58)"
you can insert the value from a cell
Worksheets("Stats").Range("B14:B1868").Formula = "=MIN('" & Worksheets("Stats").Range("A2").Value & "'!B58:IQ58)"
(I added apostrophes around the sheet name as well, just in case it contained spaces or other special characters.)

Populating cells with text using VBA

I want to select a value from one sheet and put it in a cell on a different sheet.
My script determines the proper value but I cannot get the value into the sheet.
This function returns the !Value error on the 6th line of the following excerpt:
Function PrintTest(Cell)
Dim iRow As Integer
Dim bs As Worksheet
Set bs = ThisWorkbook.Sheets("By System")
iRow = Cell.Row
bs.Cells(iRow, 6).Value = "Hello World"
End Function
I also tried using .Text.
Note: In the actual Script the text will be seeded from the other sheet and I have it stored in a variable. I am not looking for a way to get the same text into many different cells.
Update: Cell is passed from an Excel spreadsheet as an empty cell G4. Row is defined as 4. To call the functions I have been typing "=PrintTest(G4)" in my Excel worksheet named "By System" .
Update 2: Scott Holtzman answered the question in a comment. You cannot write to cells from a UDF called from within a cell. The fix was to call it from a button.
Set a cell value with Cells(Row, Col).Value="Some text"
If your sheet is active you do not need to fully qualify the address. If you activate the sheet you can just use Cells all of the time. If you have to retrieve data from a different sheet then you will have to qualify the address with the sheet name, i.e. Sheets("My Sheet").Cells(Row, Col).Value.
Also your code has Row=Cell.Row, but you are not saying what cell is active. In that case you are getting some arbitrary value. So that is where your error is, probably. Cell.Row=????? Also Row is an excel word. Use something like intRow for your variable. For example intRow=25. Cells(intRow, 6).Value="My Cell". If the cursor is on a cell you can say intRow=ActiveCell.Row.
When you have a problem such as this set a break point in your code (in your example, the Set statement). Thenm run your code. When it stops at the set statement you can hit F8 to step through one line at a time and examine your variables. Then you will see if Row is really a number or just garbage.
Hope that helps

vba excel vlookup: lookup value in stored array, lookup table in excel, output value to array

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.

SumIf with Strings?

This may be a stupid question, and if it is, I apologise. I have a table in excel:
Column a...........Column b
1 property1.......problem x
2 property2.......problemy
3 property3.......problemz
4 property1......problem a
I was wondering if I could use sumif (or any similar formula) to add the problems, referring to a certain property, in one cell. for ex:
I would have
Column a...........Column b
1 property1.......problem x problem a
The problem is I can't figure out where to start. I tried using sumif but I get an error. Probably because I'm trying to add strings. I tried to mix a vlookup with sumif but that didn't produce anything too. Im stuck here. Thanks for any help!
I am not 100 % sure, but I think you might need to use VBA for this. You could try to create the following custom function:
Create named ranges properties and problems in your sheet.
Click ALT+F11 to open VBA editor.
Press Insert --> Module
Write code
'
Function ConcatIF(Property As String, PropertyRange As Range, ProblemRange As Range) As String
Dim counter As Integer
Dim result As String
result = ""
Dim row As Range
counter = 1
For Each row In PropertyRange.Rows
If (Property = row.Cells(1,1).Value) Then
result = result + ProblemRange.Cells(counter,1).Value + " "
End If
counter = counter +1
Next row
ConcatIF = result
End Function
As I do not have excel on the machine I am writing this on, I could only test it on another machine, and therefore there could be spelling mistakes in this code.
Ensure that you write the code in the module you created, it cannot be written in a Sheet's code, must be module.
This function can then be called as a regular function, like sum, average and if. Create a unique list of all your properties on another sheet. Properties in column A, and then in column B you can call the cuntion. Assume row 1 is used for headings, write the following and copy down.
=ConcatIF(A2,properties,problems)
NOTE!!!! This code gets out of hand very quickly. It needs to do (number of properties) x (number of property/problem pairs) comparisons, so if this number is huge, it could slow down your sheet.
There could be faster methods, but this was from the top of my head.

Resources