So I have found and modified a macro that fits my needs, however there is one limitation. I am building a macro to search medical payment data for specific diagnosis codes and procedure codes. In the project I am currently working on there are only 14 diagnosis codes, so I was able to put this directly into the VBA. However, there are over 800 procedure codes which I cannot fit into the VBA.
I was able to do a seperate VBA step to bring in a table with this data, but I cant seem to get it set up to search against the table. But that being said, what is the best way to run this VBA search for such a large number of items?
Sub PROCEDURE_1_search()
Dim FirstAddress As String
Dim MySearch As Variant
Dim myColor As Variant
Dim Rng As range
Dim I As Long
MySearch = Array("412", "4100", "4101", "4102", "4103",...) <-- have over 800
With Sheets("All Claims by Date of Service").range("G5:G55000")
For I = LBound(MySearch) To UBound(MySearch)
Set Rng = .Find(What:=MySearch(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
With ActiveSheet.range("B" & Rng.Row & ":O" & Rng.Row)
.Font.ColorIndex = 1
.Interior.ColorIndex = 4
End With
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
End Sub
I might be coming up with an answer and not asking the right questions. Please let me know if there is anything I can clarify and thank you in advance for any assistance.
-Ryan
For searching an array, I would recommend you to dump the data into a variant Array instead of iterating through ranges. That way it reduces the traffic of going back on forth on the code and sheet - specially formatting. Formatting is anyway expensive, in your case it seems to cost you a moon..
So here is how it goes by steps: (not the code - if you need a code take a look at these samples.).
Transpose the data into a variant array
Search as you desire in VBA code
Dump the databack in the location (range)
Format (range)
In your example you could use AutoFilter like this to highlight rows from columns B to O where G falls between 4101-4103 in a single shot (ie four criteria match a single conditon). A minor adaption would be to call this code block for different criteria such as a standaline 412 etc.
Sub Smaller()
Dim rng1 As Range
Set rng1 = Sheets("All Claims by Date of Service").Range("$G$5:$G$55000")
With rng1
.AutoFilter Field:=1, Criteria1:=">=4100", Operator:=xlAnd, Criteria2:="<=4103"
.Offset(0, -6).Resize(rng1.Rows.Count, 14).Font.ColorIndex = 1
.Offset(0, -6).Resize(rng1.Rows.Count, 14).Interior.ColorIndex = 4
End With
Sheets(rng1.Parent.Name).AutoFilterMode = False
End Sub
Related
I am sorry if this sort of questions has already been asked, I couldn't find the answer I needed and I am quite new to VBA.
I am trying to match some values from one table to the other via an Index Match which is moving between 2 Workbooks. To do this properly I have used two loops For To. But my Code is really slow when it comes to a few thousands lines. Can I improve it with an Array or something else?
Dim mainWB As Workbook
Dim mainWS As Worksheet
Set mainWB = ActiveWorkbook
Set mainWS = mainWB.Sheets(1)
Dim RowsToProcess As Long
RowsToProcess = mainWS.Range("C" & Rows.Count).End(xlUp).Row
Dim lastCol As Long
lastCol = mainWS.Cells(10, Columns.Count).End(xlToLeft).Column
Range(Cells(11, 8), Cells(RowsToProcess, lastCol)).Select
Selection.NumberFormat = "General"
For i = 1 To lastCol
For a = 1 To RowsToProcess
If Workbooks("template.xls").Sheets("#").Cells(10 + a, 7 + i).Value <> vbNullString Then
Cells(10 + a, 7 + i).Select
ActiveCell.FormulaR1C1 = _
"= "Long Formula" "
End If
Next
Next
For my Long formula. It is basically doing 2 Index(Match. between this Workbook and 2 others, but I thought I would take it out here to keep it clearer
Thank you very much for the help!
If you are effectively defining a range you can use the first and last columns and rows addresses and create a range object.
See examples here: Dynamic ranges
You can then do:
Range(myRange).SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "LongFormula"
which effectively replaces all blank cells with your formula without having to do all the loops.
Combine those steps with the items mentioned in the comments such as avoiding .select.
Summary:
Avoid the .select
Use WITH where possible when working with objects
Get the first cell address and last cell address and create your range object
Use the special cells method to set blanks in this range to your formula.
I am trying to put the information from a worksheet into an array, using:
WBArray = ActiveSheet.Range(Cells(5, 1), Cells(end1, 29)).Value
But there are a couple of problems: first that the last 2 rows of my original range contain useless data. How could I remove these two rows as I pass them to array? is there a direct method like the one I am using?
Second, that my range starts at row 5, but since it has to go through old files, that number might be different. Is there a way I can make a floating reference, so it changes based on where the data I want begins? (for example, if it starts in row 7).
I already tried looping through each item, but that also does not seem to work.
Any ideas?
edited after OP's clarifications
you could use this function:
Function GetArray(sht As Worksheet) ', uselessRows As Long)
With sht
GetArray = .Range(.Columns(1).Find(what:="Identifier", lookat:=xlWhole, LookIn:=xlValues), _
.Cells(.UsedRange.Rows(.UsedRange.Rows.Count).Row - 6, 29)).Value
End With
End Function
and call it in your main sub as:
WBArray = GetArray(mySht)
where mySht is any valid worksheet reference like, for instance ActiveSheet
Maybe
Sub x()
Dim start1 As Long, end1 As Long, WBArray, r As Range
With ActiveSheet
Set r = .Columns(1).Find(What:="Identifier", After:=.Cells(.rows.Count,1), LookAt:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False)
If Not r Is Nothing Then
start1 = r.Row
end1 = .Range("A" & Rows.Count).End(xlUp).Row - 2
WBArray = .Range(Cells(start1, 1), Cells(end1, 29)).Value
Else
MsgBox "Identified not found"
End If
End With
End Sub
I've seen multiple posts about this, but none seem to directly address what i'm looking to do (in a way that I understand atleast).
I'm looking to iterate through my various sheets and format the data in each sheet into a table; seemingly very simple, but i'm struggling with storing the sheets into an array for easy iteration through a for loop.
I'm passing in the formatSheetsArray from the main, which has the following data stored in it: formatTablesArray = Array(lo.Name, wa.Name, sevenA.Name, oh.Name, at.Name, ob.Name, ra.Name, cvr.Name, ln.Name)
The sheets have all had their code names changed for easier manipulation, but now i'm confused how I should iterate through them from an array standpoint. There are other sheets in the workbook so I can't use sheets.count and I attempted to use For Each Worksheet In formatSheetArray and I received the same error I'm receiving now, which is Error 424 Object required. Any thoughts?
Private Sub FormatOiTables(ByVal formatSheetsArray As Variant, ByRef cmeBook As Workbook)
Dim loopCounter As Integer, lastRow As Integer
For loopCounter = 0 To UBound(formatSheetsArray)
lastRow = cmeBook.Sheets(formatSheetsArray(loopCounter)).Cells(formatSheetsArray(loopCounter).Rows.Count, "A").End(xlUp).Row
Next
End Sub
You can make and use an array of worksheet-objects:
formatTablesArray = Array(lo, wa, sevenA....)
and then do
for each tbl in formaTablesArray
tbl.
next
Solution that I just got working for the problem above. As #Siddhart pointed out as well I had a typo for my cmeDataBook Workbook, which was initially typed in as cmeBook. Thanks to all!
Private Sub FormatOiTables(ByVal formatSheetsArray As Variant, ByRef cmeDataBook As Workbook)
Dim WshtNameCrnt As Variant
Dim lastRow As Integer
For Each WshtNameCrnt In formatSheetsArray
cmeDataBook.Sheets(WshtNameCrnt).Activate
With Worksheets(WshtNameCrnt)
lastRow = .Cells(Worksheets(WshtNameCrnt).Rows.Count, "A").End(xlUp).Row
End With
MsgBox (lastRow & " " & WshtNameCrnt)
Next WshtNameCrnt
End Sub
Hello again community,
After I got so much help from you with my last Problem, that promted me to rework the entire code in a more efficient manner, I would like to ask two more questions regarding the same Project.
(1) I would like to implement a live-filter in my listbox CGList1, which is connected to the textbox SearchCGList1. Whenever someone types in the textbox, the results in the listbox should be adjusted. I found this Article on your website, as well as this Article 3 on an external Webpage. However, due to my very limited skills, I have not been able to adapt it properly. More later.
(2) After multiple items from the same listbox CGList1 have been transferred to the second listbox CGList2 via a button (which works like a treat), I would like to save them in the same cell (Range "BM") on my Worksheet Meta DB. For this problem I also used Google extensively and tried to adapt the findings (see links below) for my code - without success.
I hope that the Patient ones amongst you can help me out once again, in the knowledge that I am trying to learn as much as possible. My Problem is that for a lot of things, I simply do not know what to look for.
My preliminary code for Problem 1:
CGList1 and CGList2 have no code. They are populated in the Userform_Initialize sub via:
'Fill Material Groups Listbox1 dynamically
Dim cell As Range
Dim rng As Range
With ThisWorkbook.Sheets("Commodity Groups")
'Range to 500 in order to allow for further additions
Set rng = .Range("A2", .Range("A500").End(xlUp))
End With
Me.CGList1.ColumnWidths = "20;80"
For Each cell In rng.Cells
'Filter out blanks
If cell <> "" Then
With Me.CGList1
.AddItem cell.value
.List(.ListCount - 1, 1) = cell.Offset(0, 1).value
End With
End If
Next cell
I cannot just use .AddItem and then filter through the columns like you find in many examples online because it needs to be dynamic and there are many blanks in between the selection items on the Worksheet.
The buttons:
Private Sub addCGbutton_Click()
For i = 0 To CGList1.ListCount - 1
If CGList1.Selected(i) = True Then
'Copy only CG Name, not respective number/letter combination (only more work to cut out when working with it later)
CGList2.AddItem CGList1.List(i, 1)
End If
Next i
End Sub
'Delete selected Commodity Groups from List 2 for re-selection
Private Sub delCGbutton_Click()
Dim counter As Integer
counter = 0
For i = 0 To CGList2.ListCount - 1
If CGList2.Selected(i - counter) Then
CGList2.RemoveItem (i - counter)
counter = counter + 1
End If
Next i
End Sub
After a lot of trial and failure trying to adapt the linked approaches from other people, I tried something more simple:
Private Sub SearchCGList1_Change()
'Only show with textbox matching items in CGList1 (filter)
Dim strSQL As String
strSQL = "SELECT fieldname FROM table WHERE fieldname = "
strSQL = strSQL & "'" & Me!SearchCGList1 & "*'"
strSQL = strSQL & " ORDER BY fieldname;"
Me!SearchCGList1.RowSource = strSQL
End Sub
But without success.
Regarding Problem 2:
To save the multiple selections from CGList2 in Range BM on Worksheet "Meta DB", I toyed around a lot and my last try was:
Save multiple selections from Commodity Group List 2 to the same cell in Excel
Dim listItems As String, c As Long
With CGList2
For c = 0 To .ListCount - 1
If .Selected(c) Then listItems = listItems & .List(c) & ", "
Next c
End With
Range("BM") = Left(listItems, Len(listItems) - 2)
Usually, all my other UserForm entries are saved with a single command button in the following fasion:
Private Sub CommandButton21_Click()
'Application.ScreenUpdating = False
'Define all relevant WBs we will be working with
Dim wbInput As Workbook
Dim wb As Workbook
Set wb = ActiveWorkbook
Dim ws As Worksheet
Set ws = wb.Worksheets("Meta DB")
Dim LastRow As Long
'Save Userform Inputs
With ws
.Activate
LastRow = ws.Range("A" & Rows.Count).End(xlUp).row + 1
.
.
Range("BK" & LastRow).value = Me.payinfo90
Range("BL" & LastRow).value = Me.payinfo90more
'Risk Management - Residual Information
Range("BM" & LastRow).value = Me.CGList2
Range("BN" & LastRow).value = Me.suppsince
.
.
End With
End Sub
Again, I thank everyone who took the time to read my post and answer with tips on what to improve.
Everyone have a great day.
Using a helper column with array formula.
So if say you had your data for the 1st list box in a1:a10 and the selection from this listbox is placed in D1, the 2nd complete listbox selections are in B1:B10, but not used, then in E1:E10, I have the following array formula filled down, so you would populate the 2nd listbox off the helper column E.
Beginning with
=INDEX($B$1:$B$10,SMALL(IF(LEFT($B$1:$B$10,LEN($D$1))=$D$1,ROW($B$1:$B$10),""),ROWS($E$1:$E1)),1)
Containing
=INDEX($B$1:$B$10,SMALL(IF(NOT(ISERR(SEARCH($D$1,$B$1:$B$10))),ROW($B$1:$B$10)),ROWS($E$1:E1)),1)
You need to press CTRL SHIFT and ENTER for array formula.
I want to select a array of sheets using the Sheets(Array()) method.
The sheets I want to select are named in the cells of my workheet Printlist.
The sheetnames are listed form column D to K.
Not all cells are filled so if I use the folowing function it errors on the rows with blank cells. How can I avoid this error:
This is what the sheet looks like:
And this is the code
Sub PDF_maken()
Dim ws As Worksheet
Dim LR As Long
Dim r As Range
Dim Mypath As String
Dim strarray As String
Set ws = ActiveWorkbook.Worksheets("Printlijst")
LR = ws.Cells(Rows.Count, 1).End(xlUp).Row
For Each r In ws.Range("B20:B20").Cells
If Not IsEmpty("B" & r.Row) Then
Mypath = ws.Range("B" & r.Row).Text
colCheck = 4
Do Until Cells(r.Row, colCheck) = ""
strarray = strarray & IIf(colCheck > 4, ",") & """" & Cells(r.Row, colCheck).Value & """"
colCheck = colCheck + 1
Loop
ActiveWorkbook.Sheets(strarray).Select
ActiveWorkbook.SelectedSheets.ExportAsFixedFormat _
Type:=xlTypePDF, _
Filename:=Mypath & ws.Range("C" & r.Row).Text & ".pdf", _
Quality:=xlQualityStandard, IncludeDocProperties:=True, _
IgnorePrintAreas:=False, OpenAfterPublish:=False
End If
Next r
End Sub
You can use a regular array rather than the Array() function to create the array. Then you can loop through the cells that contains sheet names and only add them if they're not blank. Here's an example.
Sub PDF_maken()
Dim ws As Worksheet
Dim lLastRow As Long
Dim rMap As Range
Dim sPath As String
Dim aSheets() As String
Dim lShCnt As Long
Dim rSh As Range
Set ws = ActiveWorkbook.Worksheets("Printlist")
lLastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
For Each rMap In ws.Range("B2:B" & lLastRow).Cells
'Make sure there's a path
If Not IsEmpty(rMap.Value) Then
sPath = ws.Range("B" & rMap.Row).Text
're-dimension an array to hold all the sheet names
ReDim aSheets(1 To Application.WorksheetFunction.CountA(rMap.Offset(, 2).Resize(1, 8)))
'reset the counter
lShCnt = 0
'loop through all the cells that might have a sheet name
'and add them to the array
For Each rSh In rMap.Offset(, 2).Resize(1, 8).Cells
If Not IsEmpty(rSh.Value) Then
lShCnt = lShCnt + 1
aSheets(lShCnt) = rSh.Text
End If
Next rSh
ActiveWorkbook.Sheets(aSheets).Select
ActiveSheet.ExportAsFixedFormat xlTypePDF, sPath & rMap.Offset(0, 1).Text & ".pdf"
End If
Next rMap
ws.Select
End Sub
If you get Error 9: Subscript Out of Range there are three things to check:
The first one is that you spelled a sheet name wrong. Make sure there are no spaces or other funny business that makes it look like you have a good sheet name and you don't.
Next, make sure you qualify all of your references back to the workbook level. Depending on where your code is, unqualified references can point to different places. Don't ever use Sheets(). Always use ThisWorkbook.Sheets() or some other workbook reference. That will make sure you're not trying to access a sheet in a workbook that you didn't intend to.
Finally, you can get that error if you pass numbers to Sheets because your sheet names are numbers. Or rather they look like numbers, but they're really text. sheets(array(1234,4567)).select is different than sheets(array("1234","4567")).select. You have to pass strings to Sheets or you'll get that error. Kind of. You can pass numbers, but it will Select the sheets based on their index numbers rather than their names. That's why you have to be extra careful when your sheet names look like numbers.
Do a similar loop,
something like
colCheck=4
do until cells(r.row,colCheck)=""
strArray=strarray & iif(colCheck>4,",","") & cells(r.row,colCheck).value
colCheck=colCheck+1
loop
then you'll get something like a,b,c I've not tested this, so may need some tweaking. I'll revisit in a moment.