Formula to Find Match Using Wildcard and Multiple Criteria - excel

I'm trying to make a formula with multiple criteria and wildcards that will return the matching category if found. I think this would be done through a robust Index-Match formula but I'm struggling to get all the criteria into a working formula. I'll explain better with examples...
I have two worksheets, sheet "Import Data" and sheet "Invoice Coding". The formula would be entered on sheet "Import Data", in any column off to the side, say formula goes into Column AD.
Here is the "Import Data" sheet:
Here is the "Invoicing Coding" sheet (there are over 500 rows and growing, so this is a small screenshot):
Step one would be to look for the "ACCT" number (Column O) from "Import Sheet" against the "Invoicing Coding" sheet, which would usually have multiple matches. Step two would then check the corresponding "INV" on the "Import Data" sheet and see if any of the wildcard invoices on sheet "Invoice Coding" match. The return would be the "Category" (Column D) from the "Invoice Coding" sheet.
I'll provide a specific example to try to explain better:
On this line, I want to use column O data, which is the number 50000.
Lookup 50000 on the "Invoice Coding" sheet, which has many results.
Then use the INV from Column D on the previous screenshot, which is...
To find if it has a partial match to the wildcards listed in Column C of the "Invoice Coding" sheet screenshot just above.
So in this specific example, the ACCT and INV from the "Import Data" sheet match row 178 on the "Invoice Coding" sheet. The expected result from the formula would be the Category from Column D; Third Party.
Here is what I have for a formula so far, which does not incorporate the partial invoice match using wildcards:
=INDEX('Invoice Coding'!A2:E514,MATCH('Import Data'!O2,'Invoice Coding'!A2:A514,0),4)
This technically returns a Category but it doesn't use the INV # vs the partial INV with wildcard, so the return may be incorrect.
I hope my explanation makes sense. Any advice on if I can enhance an Index-Match formula to include all the required criteria lookups?
As a side note, I would then repeat this formula with slight tweaks to also return the Sub-Category from Column E of the "Invoice Coding" sheet.
Thank you all so much in advance!

going to answer my own question. We found a solution that works for us through VBA. Originally I was hesitant to approach with VBA because I thought it would take much longer to run compared to a formula, but our solution is quicker than expected, taking about 30 seconds to run through 60,000 rows.
So, our VBA solution:
'***** Declare variables to be used *****
Dim ImportWS As Worksheet, ilastrow As Long, ilooper As Long, FindArray As Variant, CodingWS As Worksheet, clastrow As Long, CodeArray As Variant, clooper As Long, acct As Long, Inv As String, matchfound As Boolean
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Import Data sheet prep and storing in FindArray
Set ImportWS = ThisWorkbook.Sheets("Import Data")
ilastrow = ImportWS.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
ImportWS.Range("AC1") = "Category"
ImportWS.Range("AD1") = "Sub Category"
ImportWS.Range("AE1") = "Billing Name"
ImportWS.Range("AC2:AE" & ilastrow).ClearContents
FindArray = ImportWS.Range("AC1:AE" & ilastrow)
'Storing Coding sheet data into array
Set CodingWS = ThisWorkbook.Sheets("Invoice Coding")
clastrow = CodingWS.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
CodeArray = CodingWS.Range("A1:F" & clastrow)
With ImportWS
'Looping through all rows on Import Data sheet...
For ilooper = 2 To ilastrow
'Storing GL Acct# and Invoice# for comparison against Coding sheet
acct = Val(.Range("O" & ilooper))
Inv = .Range("D" & ilooper)
'matchfound is a boolean that is reset on each row.
'if a match is found against the Coding sheet it is flipped to true...
'we stop looking in the following loop...
'and we commit the cat and subcat to the FindArray variant.
matchfound = False
'This is our looper for going through the coding table in the following loop.
clooper = 2
'While we haven't found a match for GL# and Inv...
'and we haven't reached the end of the table...
While matchfound = False And clooper <= clastrow
'If acct# matches coding table we perform another check...
If Val(CodeArray(clooper, 1)) = acct Then
'...for partial match on Inv - which accounts for wildcard placement as on the table...
If Inv Like CodeArray(clooper, 3) Then
'If match is found, matchfound is true, which will stop the next iteration of the loop
matchfound = True
'and we store the cat and subcat in our FindArray
FindArray(ilooper, 1) = CodeArray(clooper, 4)
FindArray(ilooper, 2) = CodeArray(clooper, 5)
FindArray(ilooper, 3) = CodeArray(clooper, 6)
End If
End If
'If no match, check the next row on the Coding table.
clooper = clooper + 1
Wend
'After we found a match or reached the end of the coding table...
'...we move on to the next row on the Import Data sheet.
Next ilooper
'After looping through all rows on the Import Data sheet we commit what we found to columns AC, AD, AE.
ImportWS.Range("AC1:AE" & ilastrow).Value = FindArray
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True

Related

Need to compare two column and provide output to third column

I want to write VBA code that will compare two columns from two different sheets.
I have data in Sheet1 column B and in Sheet2 column B.
The formula to compare both columns is in Sheet2: =B2=Sheet1!B2.
Could you please help me to write VBA code for the above formula.
I am not sure how to use the above formula in VBA code.
The basic code to compare is
If Sheet1.Range("B1").Value = Sheet2.Range("B1").Value Then
'Code to execute when criteria is met
Else
'Code to execute when criteria is not met
End If
The else part is optional and can be omitted if you don't need it
If you want to compare the full column there are a few ways to do it.
My favorite is following:
Dim iLastRow As Integer
iLastRow = Sheet1.Cells(Sheet1.Rows.Count, 2).End(xlUp) 'Gets the last row
For i = 1 To iLastRow 'Compares each row and executes the code if
If Sheet1.Range("B" & i).Value = Sheet2.Range("B" & i).Value Then
'Code to execute when criteria is met
Else
'Code to execute when criteria is not met
End If
Next i
If you want to compare the displayed/formatted text of the cell and not the value behind it use .Text instead of .Value (e.g. "10th Sep. 2019" instead of 43718)

Sorting places my data with empty cells above it

I have written a bunch of VBA macros to get my data formatted how I need it, and the last step is to sort by this new column I have generated in ascending order. However, when I hit sort by the new column, the code now places all the empty cells above my newly generated column as I think it is reading the empty as a 0 and sorts it above any alphanumeric data. This is happening because of the UDF I have for sorting the data. I need to insert the new column with the UDF for each new cell that I insert, but I don't know how to define the range in the new column.
I am close to solving this but would love some help.
Essentially what I have tried for placing the data in a new column works, but the way I have set the range is placing it in a bad spot and it can easily be sorted in the wrong order now. I include all of my code, but the issue is in the last portion of it where I am setting a range to place the new data.
I think what is happening is when I set my range from C3-C2000 and populate it, the remaining empty cells are now included in my sort and give me "lower" numbers when I sort it ascending. Thus all the empty cells are ranked higher up in the column.
Option Explicit
Sub ContractilityData()
Dim varMyItem As Variant
Dim lngMyOffset As Long, _
lngStartRow As Long, _
lngEndRow As Long
Dim strMyCol As String
Dim rngCell As Range
Columns("B:B").Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove 'make new column for the data to go
lngStartRow = 3 'Starting row number for the data. Change to suit
strMyCol = "A" 'Column containing the data. Change to suit.
Application.ScreenUpdating = False
For Each rngCell In Range(strMyCol & lngStartRow & ":" & strMyCol & Cells(Rows.Count, strMyCol).End(xlUp).Row)
lngMyOffset = 0
For Each varMyItem In Split(rngCell.Value, "_") 'put delimiter you want in ""
If lngMyOffset = 2 Then 'Picks which chunk you want printed out (each chunk is set by a _ currently)
rngCell.Offset(0, 1).Value = varMyItem
End If
lngMyOffset = lngMyOffset + 1
Next varMyItem
Next rngCell
Application.ScreenUpdating = True
'Here is where my problem arises
Range("C:C").EntireColumn.Insert
Dim sel As Range
Set sel = Range("C3:C2000")
sel.Formula = "=PadNums(B3,3)"
MsgBox "Data Cleaned"
End Sub
What I would like instead is a way to insert a new column, then have my UDF "PadNums" populate each cell up to the last cell of the previous column, essentially re-naming all my data from the previous column. I can then sort by the new column in ascending order and my data is in the correct order.
I think perhaps what I should do is copy column B into my newly inserted column C, then use some sort of last row function to apply the formula in all cells. That would give me the appropriate range always based on my original column?
I solved this! What I did was use range and xlDown to last row on column B, then pasted it to C, then inserted my UDF into C using the xlDown range!

Wildcard search on Excel

Im creating a macro in excel that has search function. so basically. i will put the list of servers in Sheet3 Column A and it will search the servers ing Sheet1 column A. Once done, it will display the result in Sheet 2 column A. However, im having issue in searching for wildcard. for example. i want to seach server "ABC123" but it will not display the result because it needs to be the whole name of the server "ABC123.def" can anyone help me with the codes?
Option Explicit
Sub HostNameFinder()
Dim srchLen, hName, nxtRw As String
Dim g As Range
'Clear Sheet 2 and Copy Column Headings
Sheets(2).Cells.ClearContents
Sheets(1).Rows(1).Copy Destination:=Sheets(2).Rows(1)
'Determine length of Search Column from Sheet3
srchLen = Sheets(3).Range("A" & Rows.Count).End(xlUp).Row
'Loop through list in Sheet3, Column A. As each value is 'found in Sheet1, Column A, copy it top the next row in Sheet2
With Sheets(1).Columns("A")
For hName = 2 To srchLen
Set g = .Find(Sheets(3).Range("A" & hName), lookat:=xlWhole)
If Not g Is Nothing Then
nxtRw = Sheets(2).Range("A" & Rows.Count).End(xlUp).Row + 1
g.EntireRow.Copy Destination:=Sheets(2).Range("A" & nxtRw)
End If
Next
End With
End Sub
If you fire up the macro recorder and perform a search for "ABC123" with the option to look in formulas and the option "Match entire cell contents" NOT selected, Excel will find the cell with the value ABC123.def
You could then study the code created by the macro recorder and would notice the difference to your code. You use lookat:=xlWhole, i.e. you're looking at the whole cell. Change it to LookAt:=xlPart. This is the equivalent of using a wildcard.
Or build the search term by wrapping it in a set of asterisks.
Set g = .Find("*" & Sheets(3).Range("A" & hName) & "*", lookat:=xlWhole)

VBA to delete rows from a worksheet based on 2 conditions on another worksheet

I have searched the web and tried all possible solutions, but in vain.
There are auditors looking at specific customers in specific areas.
I have three worksheets with data on it: Summary sheet, Raw Data sheet and an Area Listing sheet. The data gets transferred from the data sheet to the summary sheet, but contains areas that do not belong to the specific auditor. The area listings have an indicator Yes/No that must be used to clear the summary sheet of unwanted areas.
I therefore need to delete the unwanted areas from the Summary sheet by first matching the area names on both sheets and then delete those that are marked with a "No" on the area listing sheet.
The core coding I have tried:
If ActiveCell.Formula = "=VLookup(B2,Areas!C2:D,1,False)"=True _
And Sheets("Areas").Range("D2").Value = "No" Then
Sheets("Summary").EntireRow.Delete
Where B2 is the column in Summary containing the Areas and C2 is the corresponding column in Areas with D2 the column in Areas listing the selections of Yes / No.
How do I then write the code to delete the Rows in Summary where the areas are matching in the Summary and the Areas sheet and the indicator in the Areas sheet is "No"?
I have also tried:
For I = LastRowCheck To 1 Step -1
If Sheets("Summary").Range("B" & LastRowCheck).Value = _
Sheets("Areas").Range("C" & sdRow).Value _
And Sheets("Areas").Range("D" & NoRow).Value = "No" Then
If DelRange is Nothing Then
Set DelRange = Sheets("Summary").Rows(i)
End If
If not DelRange Is Nothing Then DelRange.EntireRow.Delete
End If
Next i
Can somebody please tell me where I am missing the boat?
it seems to me, that the areas sheet is going to have.. lets say 50 different areas, and the summary sheet is going to have however many rows, each with an area from the area sheet. so multiple rows would have the same area. so, you want to loop through all the rows on the summary sheet, find the corresponding area from the area sheet, and see if that area has a "no" value in column D.
given the above understanding, the following 2 options should both work:
A)
loop through all rows on areas sheet and load all area names with D
column value of "No" into an array.
loop through all rows on summary sheet
use inner loop to check and see if current row in summary sheet is
found in array of "bad areas"
delete entire row
B)
use a loop to find all rows in the area sheet with a D column value of "No" and load the names from each of these areas into an array.
use said array as the criteria for a data filter, filtering the area name column in the summary sheet
delete all visible rows (ones with areas found on areas sheet with D column value of "No"
A Code:
Dim strArray() as variant
ReDim strArray(1 to 1)
dim deleted as boolean 'keeps track of whether row was delete or not
Sheets("Area Sheet").Activate
Range ("A1").Activate 'where assuming column a has area name
for i = 1 to lastrow
if ActiveCell.offset(0, 3).formulaR1C1 = "No" then 'column D, I have had bad experience with .value so i always use formulaR1C1
strArray(i) = ActiveCell.formulaR1C1
end if
ActiveCell.offset(1,0).activate
next
Sheets("Summary Sheet").activate
Range("A1") ' again, assuing a has area name
for i = 1 to endrow
delete = false 'reset delete before inner loop
for j = 1 to ubound(strArray)
if ActiveCell.formulaR1C1 = strArray(i) then
ActiveCell.entireRow.delete xlShiftUp
deleted = true
exit for 'exits inner loop
else
deleted = false
end if
next
if not deleted then ActiveCell.offset(1,0).offset ' move down to next row if curent row was not deleted
next
B Code:
'use same as A code to get all areas with D Column "No" into array
Dim rng as Range
Range("A1").activate ' after activating summary sheet, again, assuming a has area name
ActiveCell.entireColumn.select
selection.AutoFilter Field:=1, Criteria1:=strArray, Operator:=xlFilterValues ' this will filter so only rows with area and column D of "No" will be visible.
set rng = Range("A1", Cells(lastrow, lastcolumn)).SpecialCells(xlCellTypeVisible) ' this will get all visible cells (ones that are visible with current filter condition)
rng.entirerow.delete xlshiftup 'will delete all rows in rng
depending on the amount of data, A) with application.Screenupdating =false may be faster than filter (use doesnt see whats happening behind the scenes.) do make sure to do Application.screenupdating = true again after to turn that back on.
code may need tweaking for spelling mistakes etc.. but basis should be there
I did not set endrow ever as everyone has their own way of getting the last row.
HTH
good luck!

Excel Min Date in a given range

I have an Excel file with multiple columns. If you observe the attached image the 1st column is coupons (repeated with different settlement dates). I need to write a macro which will loop through the file, and find one record for each coupon with the minimum date of all the dates that particular coupon has. For example, coupon 2 has 4 records in the attached image. I should delete three off them, and have only one record with the earliest date among those four.
Can someone please provide me an example?
One possibly is to use a temporary array formula. Assuming field Coupon is column B and Date is column C then in the next free column, say column N use {=IF(C2=MIN(IF($B:$B=B2,$C:$C)),TRUE,FALSE)}
Then use an advanced filter to filter on Coupon and TRUE in Column N. In this example I've set up the criteria and output from column Q
Eg VBA Code example
Sub test()
Dim rng As Range, strR1c1 As String
'identify minimum date using array formula
With Sheet1
.Range("N1").Value = "Temp Header"
'array formula = {=IF(C2=MIN(IF($B:$B=B2,$C:$C)),TRUE,FALSE)}
.Range("N2").FormulaArray = "=IF(RC[-11]=MIN(IF(C2=RC[-12],C3)),TRUE,FALSE)"
strR1c1 = .Range("N2").FormulaR1C1
Set rng = .Range("N2:N" & .Range("B" & .Rows.Count).End(xlUp).Row)
rng.Formula = strR1c1
rng.FormulaArray = rng.FormulaR1C1
'Advanced Filter criteria requirements to new range
.Range("B1:N11").AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=.Range( _
"Q1:AC2"), CopyToRange:=.Range("Q5:AC5"), Unique:=False
'tidy up - clear array formula
.Range("N:N").ClearContents
End With
End Sub

Resources