Index-Match with Multiple Criteria in Different Workbooks Using Variables - excel

I am trying to use variables obtain from one workbook called "Mapping File.xlsx" as criteria in an INDEX/MATCH search in a different workbook called "Extract.xlsx" (both are Sheet1). The most important thing is that I need to search Column A for PartNumber (string), Column B for GroupCounter (String), and Column C for OperationNumb (String) and return the value found in Column G for ReturnValue (String).
Setting up my variables:
Dim PartNumber, GroupCounter, OperationNumb, ReturnValue As String 'Inputs are strings that are actually pulled from first workbook
PartNumber = Workbooks("Mapping File.xlsx").Worksheets("Sheet1").Cells(2, 1)
GroupCounter = Workbooks("Mapping File.xlsx").Worksheets("Sheet1").Cells(2, 2)
OperationNumb = Workbooks("Mapping File.xlsx").Worksheets("Sheet1").Cells(5, 1)
Trying to use my variables to search a different workbook using INDEX/MATCHING:
Attempt 1
str = "=INDEX(G:G, MATCH(1, (PartNumber=A:A)*(GroupCounter=B:B)*(OperationNumb=D:D),0))"
ReturnValue = Workbooks("Extract.xlsx").Worksheets("Sheet1").Evaluate(str)
Attempt 2
str = "=INDEX(G:G, MATCH(PartNumber & GroupCounter & OperationNumb, Workbooks("Extract.xlsx").Worksheets("Sheet1").Range("A:A") & Workbooks("Extract.xlsx").Worksheets("Sheet1").Range("B:B") & Workbooks("Extract.xlsx").Worksheets("Sheet1").Range("C:C")),0))"
ReturnValue = Workbooks("Extract.xlsx").Worksheets("Sheet1").Evaluate(str)
Attempts 3 - 30
I have tried to declare the ranges and use the variable names for the ranges in the MATCH_array. I have messed with declaring workbooks and worksheets. I have tried using a SUMPRODUCT. I have tried changing my variable types multiple times. I have messed with quotation marks and splitting the string up. I have tried splitting the INDEX and MATCH functions up. I have spent about 6 hours now on the google search to see whats out there, tweaking my code, and retesting and so now I'm looking for how this can be done. I don't want to hardcode the workbook or worksheets in to the INDEX/MATCHING function if I can avoid it due to the amount of times I will be using this capability in my larger code. So I'm looking for a way to pull 3 criteria from one sheet in 3 different cells, store them as strings, and then use those three strings to search through a second workbook to find the row that it occurs on, and use that row to return the value in a different column as a string (all with using as much variable names as possible to avoid much hardcoding). Any suggestions/ideas?

How about using the autofiler?
If there is always only one record matching (or none) the selected criteria, the requested cell in column G should be always in the same row (after filtering out all other rows).
The code is not elegant, but perhaps it would give some ideas. It copies rows matching the criteria to the separate sheet and shows the contents of the first row that matches these criteria.
Sub Makro2()
Application.DisplayAlerts = False
On Error Resume Next
ThisWorkbook.Sheets("Results").Delete
ThisWorkbook.Sheets.Add.Name = "Results"
Application.DisplayAlerts = True
Sheets("Arkusz1").Select
Cells.Select
Selection.AutoFilter
Selection.AutoFilter Field:=1, Criteria1:="a" ` use PartNumber inestead of "a"
Selection.AutoFilter Field:=2, Criteria1:="b" `use GroupCounter instead of "b"
Selection.AutoFilter Field:=3, Criteria1:="c" ` use OperationNum instead of "c"
Selection.SpecialCells(xlCellTypeVisible).Copy
Sheets("Results").Paste
Var = Sheets("Arkusz1").Range("G4")
MsgBox Var
Sheets("Arkusz1").Activate
Cells.Select
Selection.AutoFilter
End Sub
Another option is to use the function CONCATENATE to join these 3 criteria and make one string of them and then use VLOOKUP function. In an additional columny put this: =CONCATENATE(cell with PartNumber, cell with GroupCounter, cell with OpetationNum) and then use Vlookup like this =VLOOKUP(cell with cruteria1& cell with criteria2& cell with criteria3;search area;column G relative address number; 0)
B C D E F G H
3 a e m `=CONCATENATE(A1;B1;C1) 1
4 a f n afn 2
5 b g o bgo 3
6 b h p bhp 4
7 c i q ciq 5
8 c j r cjr 6
9 d k s dks 7
10 d l t dlt 8
11
12
13 c j r `=VLOOKUP(A11&B11&C11;$D$1:$G$8;4;0)

Related

Assign multiple named ranges to multiple range arrays

I'm kinda new here, but here is what I'm trying to do.
I have a book lets pretend its a warehouse book for inventory, and we have different divisions in our enterprise, I have master sheet with all the goods and some sheets covering those divisions for distribution of goods between them.
My idea is to have a drop down list for each item type in book for separate divisions so i need macro to assign/reassign named range for each item.
I have 2 columns first with stock number and second with serial number , i need to put all the same serial number in the named range of one of stock numbers. if i have two or more serial numbers i need to put an array of serial numbers in named range of one stock number.
Stock numbers are in C column and serial numbers are in D column
I've tried this code
Private Sub CommandButton2_Click()
Dim LastRow As Long
Dim r As Range
LastRow = Cells(Rows.Count, "C").End(xlUp).Row
For Each r In Range("C2:C" & LastRow)
Range(r.Offset(0, 1), r.Offset(0, 1)).Name = r.Value
Next r
End Sub
But thats not realy working, and assigns only one serial number per one named range of stock numbers
================================================================
So i ran this code you proposed in your updated version and struck some problems
Private Sub CommandButton2_Click()
Dim this As Worksheet: Set this = Sheets("ALFA")'renamed this for my book'
Dim that As Worksheet: Set that = Sheets("STORAGE")'renamed that for my book'
serialNumbers = that.Range(that.Columns(3), that.Columns(4))'Could not find method Unique(and there is no mentions about'
'it in MS documentation) for Application object so i changed it to just Range'
For r = 2 To this.UsedRange.Rows.Count
buffer = ""
comma = ""
stockNumber = this.Cells(r, 3)
For x = 2 To UBound(serialNumbers)
If serialNumbers(x, 1) = stockNumber Then
buffer = buffer & comma & serialNumbers(x, 2)
comma = ","
End If
Next
this.Cells(r, 4).Validation.Delete
this.Cells(r, 4).Validation.Add _'After doing everything it strucks with Run time error 1004
Type:=xlValidateList, _ '/Application-defined or object-defined error in this
AlertStyle:=xlValidAlertStop, _'hole'
Formula1:=buffer 'block'
Next
End Sub
And sometimes code just hangs my excel application for atleast 3 mins, i think it's because there is no limit for cells to look up to and eventualy it tries to give all the cells in D:D a validation check
So if you want to set the validation, it is possible to set dynamic ranges BUT the validation won't accept a text list, for instance "one, two, three". The validation is looking for an array of values, and unfortunately it is tricky to pass a dynamic array using formulas only. You can set it up to do a dynamic range, but that would have to point to a range of cells that contain the needed values one per cell.
However, before you set all that up it's probably just easier to set the validation entirely in code. See this google sheet, which just contains the layout for reference. I have the complete list of items in Column 1 & 2 of the sheet (Item, Stock Number) and the complete list of serial numbers in columns 5 & 6 (Stock Number, Serial Number).
Then I run this code:
Sub setValidation()
Dim this As Worksheet: Set this = Sheets("demo")
Dim that As Worksheet: Set that = Sheets("lookups")
serialNumbers = Application.Unique(that.Range(that.Columns(5), that.Columns(6)))
For r = 2 To this.UsedRange.Rows.Count
buffer = ""
comma = ""
stockNumber = this.Cells(r, 3)
For x = 2 To UBound(serialNumbers)
If serialNumbers(x, 1) = stockNumber Then
buffer = buffer & comma & serialNumbers(x, 2)
comma = ","
End If
Next
this.Cells(r, 4).Validation.Delete
this.Cells(r, 4).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Formula1:=buffer
Next
End Sub
We assign some worksheet variables to make it easier to reference them, and then put the stock number/serial number combos into an array (with UNIQUE so I don't have to check for duplicates).
Then I run through the range that needs the validations (demo column 4), getting the stock number from column 3 and then using that stock number to find all serial numbers that match, concatenating them into a string and then using that string to set the validation.
Use Validation.Delete before setting the validation to avoid stacking rules.
Assuming that your version of Excel doesn't have UNIQUE, you can use INTERSECT to control the size of the serialNumbers array, like this:
Sub setValidation()
Dim this As Worksheet: Set this = Sheets("demo")
Dim that As Worksheet: Set that = Sheets("lookups")
serialNumbers = Intersect( _
that.Range(that.Columns(5), that.Columns(6)), _
that.UsedRange _
)
For r = 2 To this.UsedRange.Rows.Count
buffer = ""
comma = ""
stockNumber = this.Cells(r, 3)
For x = 2 To UBound(serialNumbers)
If serialNumbers(x, 1) = stockNumber Then
buffer = buffer & comma & serialNumbers(x, 2)
comma = ","
End If
Next
this.Cells(r, 4).Validation.Delete
this.Cells(r, 4).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Formula1:=buffer
Next
End Sub
Assuming you do have UNIQUE and FILTER in your Excel version, there is another way to do it, using the EVALUATE function to access the Excel function engine. In this case we will just write out a formula just like we would in a cell, and then evaluate it from VBA. Unless specified, evaluate occurs in the context of the active sheet, so that's what I use that.evaluate in this code:
Sub setValidation()
Dim expr As String
Dim this As Worksheet: Set this = Sheets("demo")
Dim that As Worksheet: Set that = Sheets("lookups")
For r = 2 To this.UsedRange.Rows.Count
stockNumber = this.Cells(r, 3)
expr = "Textjoin("","", true, Unique(Filter(F:F, E:E=""" & stockNumber & """)))"
serialNumbers = that.Evaluate(expr)
this.Cells(r, 4).Validation.Delete
this.Cells(r, 4).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Formula1:=serialNumbers
Next
End Sub
In this case, we use FILTER to return ONLY the serial numbers that match a stock number, UNIQUE to make sure there are no duplicates, and then TEXTJOIN to create a list from that, and then we can just pass that result straight to the validation.
===================================================
Original answer, shows how to get a list of all serial numbers for a specific stock number using only excel formulas, but it became clear that this wouldn't be sufficient, since the lists were going to be used to set validation. Left here for completeness.
So you have two columns, C and D, and you need to get a list of all values in D that match the entries in C. This is actually simple enough to not need code, but you may have more requirements. I'm going to start an answer with just a very basic set of formulas. See this google sheet.
To get a unique list of the stock numbers, I have used UNIQUE(C:C) in G1. This will produce the list in column G.
Then in column H, I have used TEXTJOIN+UNIQUE+FILTER to produce a comma separated list of serial numbers. FILTER takes one input array (in this case Col D) and filters it with another array (Col C) and a condition (the serial number) to return a list of matches, and wrapping that in UNIQUE makes sure that the result array contains only unique values. Wrapping that in TEXTJOIN converts the result array into text.
What I'm not entirely clear on is your need for a named range, or what you will do with the multiple rows in a sheet. For instance, STORAGE rows 35 & 36 are both for DDG_33:
DDG_33 0BV1111
DDG_33 0AV0951
and if you ran some code to produce a list of values and put it in D35 you would have:
DDG_33 0BV1111, 0AV0951
DDG_33 0AV0951
but you would still have two entries for DDG_33. If you ran the code again, you would have
DDG_33 0BV1111, 0AV0951, 0AV0951
DDG_33 0AV0951
and so forth in an infinite loop. It seems like you would need to take the list of unique stock numbers and put them on a different sheet, along with the list of matching serial numbers, but just clarify what you want to do and I can update my answer.

Need to adjust this

Someone from my company created this code to automatically change data within a spreadsheet. I wrote a question yesterday on here and asked about this same thing.
I think this is the code that I need to use but I need it to compare column G to F. How would I write this code to do that?
ActiveCell.FormulaR1C1 = "=(RC[-4]-RC[16])/RC[-4]*100"
-- This code compares column G to column C.
I need it to compare column G to F.
Thank you for taking the time to help me. I know nothing about code.
The formula you've given does not compare column G to column C: it compares column -4 with column 16, based on the cell where you currently are (the so-called ActiveCell in your code). This means that the difference between both columns is 20 columns, which is larger than the difference between G and C (e.g. I would rather go for columns W and C).
In fact, your formula calculates the procentual difference between the mentioned columns (from -4 to 16), e.g.
In column -4, value 5.
In column 16, value 6.
The result of your formula will be 20 (from 5 to 6, there is an increase of 20%).
Option Explicit
Sub Main()
Dim Check as Boolean
'Call the function, on the row you want
Check = Compare_Rng(1) 'Or any other integer value it will return True if same and False if not
End Sub
Private Function Compare_Rng(RowCnt as Integer) as Boolean
If Sheets("Sheet Name").Range("G" & RowCnt) = Sheets("Sheet Name").Range("F" & RowCnt) Then
Compare_Rng = True
Else
Compare_Rng = False
End if
End Function
If You want to insert formula you can do this by below
Dim RowNr as Integer
RowNr = ActiveCell.Row
ActiveCell = "=G" & RowNr & "=F" & RowNr

Nested IF statement inside For loops in VBA

My other macro scrapes mutual fund sizes from web and returns the values in Worksheets("Updater").Cells(xx,4) (so column C). The macro scrapes also the date in which the fund has been updated on morningstar and returns it in column F on the same sheet. I've got 83 fund names in column A.
On a sheet called "Tracker" I've got the fund names transposed. They start from cell (1,4) and go to (1,86). In column B I've got DATEVALUE of dates in column C. There're some issues with the date formats so I'll just hide the column B later.
The below macro works as it is, but when you uncomment lines 11 and 22, it doesn't work as intended anymore. Idea is to look at the fund sizes on sheet "Updater" and return the fund size to the corresponding date on sheet "Tracker". As said, this works.
However, with the nested if statement, I'd want to modify the macro so it would keep all the cells that contain other than "-" untouched. This way I would start to cumulate the fund size data when the macro is executed on a daily basis and doesn't overwrite the old fund sizes with "-".
Now the macro flips the funds who knows where and I simply can't solve this nested if statement problem. Is this even the correct/best way to get where I want?
Sub fundSizeTracker()
Dim f As Integer
Dim numRows As Integer
numRows = 86
f = 2 'Sheet fund names & sizes
For m = 2 To 4 'Sheet row number, starting at rowNo 2
For i = 4 To numRows 'Sheet column number (set to include all 83 funds)
' If Worksheets("Tracker").Cells(m, i) = "-" Then
' Are Datevalues same? 'Tracker datevalue 'Updater datevalue
If StrComp(Worksheets("Tracker").Cells(m, 2).Value, Worksheets("Updater").Cells(f, 13).Value) = 0 Then
Worksheets("Tracker").Cells(m, i) = Worksheets("Updater").Cells(f, 4).Value
Else
Worksheets("Tracker").Cells(m, i) = "-"
End If
If f = numRows - 1 Then
f = 2
End If
f = f + 1
' End If
Next i
Next m
End Sub

Excel Macro: check whether the current cell has a substring as another cell

Without using VBA (I can do it in VBA, but just want to try whether a macro can do it as well, but I haven't figured it out yet),
I have two sheets. Sheet A includes a column of names such as its cell like:
Wright
Sheet B includes a column of names well, but with more letters like title in one cell such as:
Mr. Wright
Sheet A to B is in a relationship of one-to-many (Wright in Sheet A might have multiple rows with Mr.Wright in Sheet B).
If in Sheet B, how to write a macro with some function to achieve: to check whether 'Mr.Wright' has a substring in a cell in Sheet A.
(I think about it might be easier to start from Sheet A: might with regex, find all matches in Sheet B with INDEX or MATCH first. It's much better if it can be done from Sheet B in one shot)
Create a macro called sub_in_name.
Option Explicit
Sub sub_in_name()
Dim x, i As Long
Dim endofcells1, endofcellsmany As Long
endofcells1 = WorksheetFunction.CountA(Range("A:A"))
endofcellsmany = WorksheetFunction.CountA(Range("B:B"))
For x = 1 To endofcells1
For i = 1 To endofcellsmany
If (InStr(1, Cells(i, 2), Cells(x, 1), vbTextCompare)) Then
Cells(i, 2 + x).Value = "True"
Else
Cells(i, 2 + x).Value = "False"
End If
Next i
Next x
End Sub
Intr(start, SearchStr, SearchInStr, vbaoption) is the main function to make this work. Cells(i, 2 + x) is indexed based off the number of non-empty cells in column "A"
Make sure to clear the cells content for each trial; after column "B".
For example put in Column "A" & Column "B" and you will get columns "C:D"
Column "A" Column "B" Column "C" Column "D"
Wright Mr. Wright True False
Roger Wright Jr. True False
Wright the Ivth. True False
Sally False False
Roughly similar logic. Originally I wanted to use existing macro functions to do it. Finally it ended up like making a customized function like below:
Inspired by another thread in stack overflow. I made a code to do full checking like user3553260's. But I think a function is not a bad choice as well, considering if the efficiency is not the top one concern here.
Function LookupName(lookupValue As Variant, lookupRange As Range) As String
Dim r As Long
Dim c As Long
Dim s As String
s = "No"
For r = 1 To lookupRange.Rows.Count
For c = 1 To lookupRange.Columns.Count
If Not IsEmpty(lookupRange.Cells(r, c).Value) Then
If InStr(LCase(lookupValue), LCase(lookupRange.Cells(r, c).Value)) Then
s = "Yes"
Exit For
End If
End If
Next
Next
LookupName = s
End Function

Excel - find a value in column A that appears less than or equal to 4 times and print in column B

I have a list of usernames sorted alphabetically in column A, with some appearing numerous times.
I want to prnt the username in column B if it appears less than or equal to 4 times.
Do I need an array to go through all the different username values in the column to find the ones that appear less than or equal to 4 times?
Consider:
Sub dural()
Dim A As Range, B As Range, v As String, K As Long
Set A = Intersect(Range("A:A"), ActiveSheet.UsedRange)
Set B = Range("B:B")
K = 1
With Application.WorksheetFunction
For Each aa In A
v = aa.Value
If v <> "" Then
If .CountIf(A, v) <= 4 Then
If .CountIf(B, v) = 0 Then
Cells(K, "B").Value = v
K = K + 1
End If
End If
End If
Next aa
End With
End Sub
Add a helper column and place the following formula in the second row:
=IF(AND(COUNTIF(A:A,A2)<=4,COUNTIF($A$2:A2,A2)=1),MAX($B$1:B1)+1,"")
And copy down:
At this point you can filter on the non Blank Cells and copy them to another range.
If you want to use a formula to get the list then Put this in another column row 2:
=IFERROR(INDEX(A:A,MATCH(ROW(1:1),B:B,0)),"")
And copy down.
No need for helper columns or VBA, just a bit of finely-tuned IF functions :)
=IF(COUNTIFS(BE:BE,BE2)<=4,IF(COUNTIFS($BE$1:BE2,BE2)=1,BE2,"0"),"0")
^Here, BE is where all your data is, starting on Row 2
What it does:
If the Name appears 4 or fewer times,
If this is the first time the Name appears in the column
Print the Name
(Otherwise insert 0)
To remove the 0 values i.e empty rows:
Paste over the formula with the same column as values (this is so you can..)
.. Replace All (Ctrl-H) "0"s with nothing "" (so that you can..)
.. Select the blank rows using Go To (Ctrl-G) > Special > Blanks
Delete (Shift Cells Up)
You can also simply Filter out the Blank/0 values

Resources