How to make a for loop more efficient in VBA? - excel

I am new to VBA and I am looking for something that is similar to python pandas, i.e. avoiding to loop through each rows many times. I am trying to achieve a quite simple task and it takes way too long. What is the best alternative to loops?
Looking around it seems that AutoFilter and Find might do, however I am not sure on what is the best option in my case.
Sub UpdateManualUpdates()
Dim lookUpSheet As Worksheet, updateSheet As Worksheet
Dim valueToSearch As String
Dim i As Long, t As Long
Set lookUpSheet = Worksheets("Manual price changes")
Set updateSheet = Worksheets("Price Build-up")
lastRowLookup = lookUpSheet.Cells(Rows.Count, "F").End(xlUp).Row
lastRowUpdate = updateSheet.Cells(Rows.Count, "B").End(xlUp).Row
'get the number of the last row with data in sheet1 and in sheet2
For i = 6 To lastRowLookup 'i = 2 to last to omit the first row as that row is for headers
valueType = lookUpSheet.Cells(i, 5) 'Type of update - Both, Planning group or GC
valueGroup = lookUpSheet.Cells(i, 3) 'Family group
valueGC = lookUpSheet.Cells(i, 4) 'GC
ValueChange = lookUpSheet.Cells(i, 6) 'What is the % change
'above get the values from the four column into variables
With Worksheets("Price build-up")
For t = 6 To lastRowUpdate
'AW is column 49 target column to update
'M is target column for group, 13
'C is target column for GC, 3
If valueType = "Both" Then
If .Cells(t, 13) = valueGroup And .Cells(t, 3) = valueGC Then
.Cells(t, 49) = ValueChange
End If
End If
If valueType = "Planning group" Then
If .Cells(t, 13) = valueGroup Then
.Cells(t, 49) = ValueChange
End If
End If
If valueType = "GC" Then
If .Cells(t, 3) = valueGC Then
.Cells(t, 49) = ValueChange
End If
End If
Next t
End With
Next i
End Sub

It is slow to access and update the Workbook object. Based on what you have now, a simple way is to convert the worksheet to an array and read the data from the array. Also, set Application.ScreenUpdating = False would make it a little bit faster.
Sub UpdateManualUpdates()
Application.ScreenUpdating = False
Dim lookUpSheet As Worksheet, updateSheet As Worksheet
Dim valueToSearch As String
Dim i As Long, t As Long
Set lookUpSheet = Worksheets("Manual price changes")
Set updateSheet = Worksheets("Price Build-up")
Dim lookUpSheetArray As Variant
Dim updateSheetArray As Variant
lastRowLookup = lookUpSheet.Cells(Rows.Count, "F").End(xlUp).Row
lastRowUpdate = updateSheet.Cells(Rows.Count, "B").End(xlUp).Row
lookUpSheetArray = lookUpSheet.Range("A1:F" & lastRowLookup).Value
updateSheetArray = updateSheet.Range("A1:AW" & lastRowUpdate).Value
For i = 6 To lastRowLookup 'i = 2 to last to omit the first row as that row is for headers
valueType = lookUpSheetArray(i, 5) 'lookUpSheet.Cells(i, 5) 'Type of update - Both, Planning group or GC
valueGroup = lookUpSheetArray(i, 3) 'Family group
valueGC = lookUpSheetArray(i, 4) 'GC
ValueChange = lookUpSheetArray(i, 6) 'What is the % change
'above get the values from the four column into variables
For t = 6 To lastRowUpdate
'AW is column 49 target column to update
'M is target column for group, 13
'C is target column for GC, 3
If valueType = "Both" Then
If updateSheetArray(t, 13) = valueGroup And updateSheetArray(t, 3) = valueGC Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
If valueType = "Planning group" Then
If updateSheetArray(t, 13) = valueGroup Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
If valueType = "GC" Then
If updateSheetArray(t, 3) = valueGC Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
Next t
Next i
Application.ScreenUpdating = True
End Sub
From my experiment, it is about 35% faster. Not a big improvement but just take a minute to update.

Related

Scripting.Dictionary to fill a column with a string taking in consideration more then 3 criteria

I am trying to fill a column C with the string consider if the consumer on the row matches one of the criteria:
If the consumer meets one of these rules, the value should be set to Consider:
• Consumer has only 1 Transaction -- (is done)
• Consumer has 2 - 4 Transactions but total volume < 10,000 USD --- (is done)
• Consumer Level (based on rule below) is Level 2 or Level 3 --- ( this information are on column CV and CW)
• If dropdown is 60 Days and max transaction date is older than 30 days
• if dropdown is 1 year and max transaction date is older than 90 days
• If dropdown is 5 years and max transaction date is older than 180 days
'Interdction Review Tab, column C
Sheets("Interdiction Review").Columns(3).Font.Bold = True
Sheets("Interdiction Review").Columns(3).HorizontalAlignment = xlCenter
'Consumer has only 1 Transaction, the value on Interdiction Review Tab on Column C will be Consider
Dim wsStart As Worksheet, lastRow1 As Long, wsFinal As Worksheet
Dim dict As Object, rw As Range, v, v2, k, m, lin
Dim wsSSart As Worksheet
Dim dateDifference As Long
Dim SStartSelection As String
Dim isConsider As Boolean
Dim valid_col(1) As Integer
Dim lvl As Boolean
Set wsSSart = ActiveWorkbook.Sheets("SStart")
Set wsStart = ActiveWorkbook.Sheets("Start")
Set wsFinal = ActiveWorkbook.Sheets("Interdiction Review")
lastRow1 = wsStart.Cells(Cells.Rows.Count, "A").End(xlUp).Row
Set dict = CreateObject("Scripting.Dictionary")
SStartSelection = wsSSart.Cells(7, "A").Value
lvl = False
For Each rw In wsStart.Range("A2:AJ" & lastRow1).Rows
v = rw.Cells(8).Value
v2 = rw.Cells(36).Value
If Len(v) = 0 Or Len(v2) = 0 Then
v = rw.Cells(7).Value
v2 = rw.Cells(35).Value
End If
dict(v) = dict(v) + 1
dict(v2) = dict(v2) + 1
Next rw
For Each k In dict
isConsider = False
m = Application.Match(k, wsFinal.Columns(1), 0)
wsFinal.Cells(m, 7).FormulaArray = wsFinal.Cells(m, 7).Formula
dateDifference = DateDiff("D", wsFinal.Cells(m, 7).Value, Date)
If dict(k) = 1 Then
isConsider = True
ElseIf dict(k) >= 2 And dict(k) <= 4 And wsFinal.Cells(m, 6).Value <= 10000 Then
isConsider = True
End If
If StrComp(SStartSelection, "60 Days") = 0 And dateDifference > 30 Then
isConsider = True
ElseIf StrComp(SStartSelection, "1 Year") = 0 And dateDifference > 90 Then
isConsider = True
ElseIf StrComp(SStartSelection, "5 Years") = 0 And dateDifference > 180 Then
isConsider = True
End If
'Client number
If wsStart.Cells(2, 8) <> "" Then
valid_col(0) = 8
valid_col(1) = 36
Else
valid_col(0) = 7
valid_col(1) = 35
End If
'Level verification
For lin = 2 To lastRow1
If wsStart.Cells(lin, valid_col(0)) = k Then
If wsStart.Cells(lin, 100).Value = "Level 2" Or wsStart.Cells(lin, 100).Value = "Level 3" Then
lvl = True
Exit For
End If
End If
If wsStart.Cells(lin, valid_col(1)) = k Then
If wsStart.Cells(lin, 101).Value = "Level 2" Or wsStart.Cells(lin, 101).Value = "Level 3" Then
lvl = True
Exit For
End If
End If
Next lin
If isConsider And lvl Then
If Not IsError(m) Then wsFinal.Cells(m, 3).Value = "Consider"
End If
Next k
End Sub
It seems that my code is looking in the wrong column to check for the clients Level. ex:
Client number 3 is located on column H so the code needs to check column CV to see the level
client number 3 is as well located on column AJ the code needs to check the Column CW to see the level.
if the client is located on both columns and cod need to check both columns for the find the information.
The level for column CV is when the client number is on column H or/and G
The level for column CW is when the client is on Column AJ or/and AI
I asked here as well (and you can download the file)
https://www.ozgrid.com/forum/index.php?thread/1228270-how-to-populate-a-column-with-a-string-taking-in-consideration-5-different-crite/&postID=1239894#post1239941
The only time that lvl is set to False is before the For Each k In dict loop ever happens.
So, once a particular row sets lvl to True within that loop, every subsequent row will also have lvl be True, because there's nothing in the loop to set lvl back to False. Try this instead:
For Each k In dict
isConsider = False
lvl = False
Your code is too large. I don't think you will get the answer you want because of the time it takes to find the problem. Therefore I will teach you how to structure your code in such a way as to be able to discuss any part of it. Please consider the code below.
Sub NewTest()
' 093
Dim WsIR As Worksheet
Set WsIR = CreateWsIR()
Worksheets("Start").Activate ' probably not useful
End Sub
Private Function CreateWsIR() As Worksheet
' 093
Dim Fun As Worksheet ' = Function return object under preparation
Set Fun = Worksheets.Add ' Excel will make this the ActiveSheet
With Fun
.Name = "Interdiction Review"
.Move After:=Worksheets("Start")
' format your sheet here
End With
Set CreateWsIR = Fun
End Function
Look at the advantages of this structure.
The first 30-odd lines of your code are compressed into just one.
This allows you to clearly develop your narrative in the main procedure.
Meanwhile everything related to creating the new worksheet is bundled into one, separate procedure which is easy to test, easy to maintain and easy to ask questions about should the need arise.
As you continue to create your project's narrative you will come to a point where the task is to populate column C. With the above method that filtering and elimination process will take place in a function which is separate just as the the function CreateWsIR is separate above. It will return one value which you will insert into a cell in the main procedure. In your present setup you can't even pinpoint where that action takes place (and neither can we). If you change the structure to make it more transparent you wouldn't have such a problem and we would be happy to assist.

The element does not meet the requirement is included in the array

I created an array to dynamically search content in column I in worksheet "gun inventory" and put corresponding information in column B into the array. Then I display these data in the array on the list created.
As you can see from my code, my condition is text in column I is "In Tool". However the last element in array does not have "In Tool" in element though still be included into the array. I have no idea why this happen and I will be appreciated if you can help.
I donno why S0007 will show here too.
Please ignore the variables undefined I defined them as public variables. The program runs well I just don't know why the last element will be included in the array.
Private Sub CheckGun_Click()
Dim gunarr()
Dim col As Integer
'Sheets.Add after:=Worksheets(5)
'ActiveSheet.Name = "tmp"
m = 0
With ThisWorkbook.Worksheets("gun inventory")
g = Application.WorksheetFunction.CountIf(.Range("I:I"), "In Tool")
Debug.Print g
ReDim gunarr(1 To g)
For i = 2 To .Cells(Rows.Count, "A").End(xlUp).row
If .Cells(i, "I").Text = "In Tool" Then
m = m + 1
End If
gunarr(m) = .Cells(i, "B")
Next i
End With
row = UBound(gunarr) - LBound(gunarr)
'Worksheets("tmp").Range("A2").Resize(row + 1).Value = Application.Transpose(gunarr)
With ListBox1
.Font.Size = 10
.ForeColor = vbBlue
.ControlTipText = "Tools are in use"
.ColumnHeads = True
.ColumnCount = Range("a1").CurrentRegion.Columns.Count
.ColumnWidths = "80"
.ListStyle = fmListStyleOption
.MultiSelect = fmMultiSelectMulti
.List = gunarr()
End With
'On Error Resume Next
'Application.DisplayAlerts = False 'prevent alert popping up for deleting the sheet
'ThisWorkbook.Sheets("tmp").Delete
'Application.DisplayAlerts = True
End Sub
As you can see from my code, my condition is text in column I is "In Tool". However the last element in array does not have "In Tool" in element though still be included into the array. I have no idea why this happen and I will be appreciated if you can help.

Excel macro to loop through range until value found, populate range below found cell with formula

I have a large table, sometimes with hundreds of rows.
This table is generated by another application that exports to excel.
One column has the heading "Adjusted Price".
I want all the rows in this column to contain a formula (presently they're all 0's).
I want to automate this process because the table gets regenerated all the time.
This column may not always be in the same place. So I need a macro that can find this column ("Adjusted Price") and then fill all the cells in that column with a formula (with the exception of the first row of course).
Can this be done?
Thanks in advance :)
Your homework is to figure out how to plug it in!
Option Explicit
Sub setAdjustedPrice()
Dim column As Integer
Dim adjustedPriceColumn As String
Dim found As Boolean
Dim rowCount As Long
column = 1
rowCount = 1
Do While Range(FncAlphaCon(column) & rowCount).Value <> "" And found = False
If (Range(FncAlphaCon(column) & rowCount).Value = "Adjusted Price") Then
found = True
adjustedPriceColumn = FncAlphaCon(column)
Else
column = column + 1
End If
Loop
If found = True Then
Do While rowCount < ActiveSheet.UsedRange.Rows.count
rowCount = rowCount + 1
Range(adjustedPriceColumn & rowCount) = "YOUR FORMULA"
Loop
Else
MsgBox ("'Adjusted Price' column not found, cannot continue.")
End If
End Sub
Private Function FncAlphaCon(aNumber As Integer) As String
' Fixed version 27/10/2011
Dim letterArray As String
Dim iterations As Integer
letterArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
If aNumber <= 26 Then
FncAlphaCon = (Mid$(letterArray, aNumber, 1))
Else
If aNumber Mod 26 = 0 Then
iterations = Int(aNumber / 26)
FncAlphaCon = (Mid$(letterArray, iterations - 1, 1)) & (Mid$(letterArray, 26, 1))
Else
'we deliberately round down using 'Int' as anything with decimal places is not a full iteration.
iterations = Int(aNumber / 26)
FncAlphaCon = (Mid$(letterArray, iterations, 1)) & (Mid$(letterArray, (aNumber - (26 * iterations)), 1))
End If
End If
End Function

Unique Count Formula for large dataset

I am having trouble determining a way to enter a 1 or 0 into an adjacent cell to indicate whether or not a value is unique when working with a large dataset. I have read of multiple methods for accomplishing this, however none of them seem efficient for my purposes: I am using an instance of Excel 2010 (so I do not have the Distinct Count feature in PivotTables, and when I try to use PowerPivot it crashes my file due to processing limitations.
In this StackOverflow question: Simple Pivot Table to Count Unique Values there are suggestions to use SUMPRODUCT or COUNTIF, but when working with 50,000+ rows as I am, this causes terrible performance and a file size of ~35 MB instead of ~3 MB. I wanted to know if there is a better solution for a large, dynamic dataset whether it is a formula or VBA.
An example of what I would like to accomplish is (with the Unique column being the adjacent cell):
Name Week Unique
John 1 1
Sally 1 1
John 1 0
Sally 2 1
I attempted to script the same functionality of COUNTIF but with no success:
For Each Cell In ThisWorkbook.Worksheets("Overtime & Type Data").Range("Z2:Z" & DataLastRow)
If Worksheets("Overtime & Type Data").Cells(Cell.Row, 26) <> Worksheets("Overtime & Type Data").Cells(Cell.Row - 1, 26) Then
FirstCell = Cell.Row
End If
If (Worksheets("Overtime & Type Data").Range(Cells(FirstCell, 26), Cells(Cell.Row, 26)) = Worksheets("Overtime & Type Data").Range(Cells(Cell.Row, 26))) = True Then
Cell.Value = 1
Else
Cell.Value = 0
End If
Next Cell
This code ran on over 130,000 rows successfully in less than 3 seconds. Adjust the column letters to fit your dataset.
Sub tgr()
Const colName As String = "A"
Const colWeek As String = "B"
Const colOutput As String = "C"
Dim ws As Worksheet
Dim rngData As Range
Dim DataCell As Range
Dim rngFound As Range
Dim collUniques As Collection
Dim arrResults() As Long
Dim ResultIndex As Long
Dim UnqCount As Long
Set ws = ThisWorkbook.Sheets("Overtime & Type Data")
Set rngData = ws.Range(colName & 2, ws.Cells(Rows.Count, colName).End(xlUp))
Set collUniques = New Collection
ReDim arrResults(1 To rngData.Cells.Count, 1 To 1)
On Error Resume Next
For Each DataCell In rngData.Cells
ResultIndex = ResultIndex + 1
collUniques.Add ws.Cells(DataCell.Row, colName).Value & ws.Cells(DataCell.Row, colWeek).Value, ws.Cells(DataCell.Row, colName).Value & ws.Cells(DataCell.Row, colWeek).Value
If collUniques.Count > UnqCount Then
UnqCount = collUniques.Count
arrResults(ResultIndex, 1) = 1
Else
arrResults(ResultIndex, 1) = 0
End If
Next DataCell
On Error GoTo 0
ws.Cells(rngData.Row, colOutput).Resize(rngData.Cells.Count).Value = arrResults
End Sub
One approach is to sort by Name and Week. Then you can determine Unique for any row by comparing with the previous row.
If you need to preserve the order, you could first write a column of Index numbers (1, 2, 3, ...) to keep track of order. After calculating Unique, sort by Index to restore the original order.
The whole process could be done manually with relatively few steps, or automated with VBA.
I'm not sure how well this will work with 50000 values, but it goes through ~1500 in about a second.
Sub unique()
Dim myColl As New Collection
Dim isDup As Boolean
Dim myValue As String
Dim r As Long
On Error GoTo DuplicateValue
For r = 1 To Sheet1.UsedRange.Rows.Count
isDup = False
'Combine the value of the 2 cells together
' and add that string to our collection
'If it is already in the collection it errors
myValue = Sheet1.Cells(r, 1).Value & Sheet1.Cells(r, 2).Value
myColl.Add r, myValue
If isDup Then
Sheet1.Cells(r, 3).Value = "0"
Else
Sheet1.Cells(r, 3).Value = "1"
End If
Next
On Error GoTo 0
Exit Sub
DuplicateValue:
'The value is already in the collection so put a 0
isDup = True
Resume Next
End Sub
Just about any bulk operation will beat a loop involving worksheet cells. You might be able to trim the time down a bit by performing all of the calculations in memory and only returning the values back to the worksheet en masse when it is complete.
Sub is_a_dupe()
Dim v As Long, vTMP As Variant, vUNQs As Variant, dUNQs As Object
Debug.Print Timer
On Error GoTo bm_Uh_Oh
Set dUNQs = CreateObject("Scripting.Dictionary")
With Worksheets("Sheet1")
vTMP = .Range(.Cells(2, 1), .Cells(Rows.Count, 2).End(xlUp)).Value2
ReDim vUNQs(1 To UBound(vTMP, 1), 1 To 1)
For v = LBound(vTMP, 1) To UBound(vTMP, 1)
If dUNQs.Exists(Join(Array(vTMP(v, 1), vTMP(v, 2)))) Then
vUNQs(v, 1) = 0
Else
dUNQs.Add Key:=Join(Array(vTMP(v, 1), vTMP(v, 2))), _
Item:=vTMP(v, 2)
vUNQs(v, 1) = 1
End If
Next v
.Cells(2, 3).Resize(UBound(vUNQs, 1), 1) = vUNQs
End With
Debug.Print Timer
bm_Uh_Oh:
dUNQs.RemoveAll
Set dUNQs = Nothing
End Sub
Previous experience tells me that the variety of data (as well as hardware, etc) will impact timing the process but in my random sample data I received these elapsed times.
 50K records ..... 0.53 seconds
130K records .... 1.32 seconds
500K records .... 4.92 seconds

VBA Excel Populating cells based on previous existence

I haven't seen this addressed yet, but I think that might be because I don't know how to phrase my problem concisely. Here's an example of what I'd like to try and do:
Given a column which holds state initials check output sheet if that state has been found before. If it hasn't then populate a new cell with that state's initials and initialize the count (number of times state has been found) to one. If the state's initials are found in a cell within the output sheet then increment the count by one.
With this, if we have a 50,000 (or however many) lined excel sheet that has states in random order (states may or may not be repeated) we will be able to create a clean table which outputs which states are in the raw data sheet and how many times they appeared. Another way to think about this is coding a pivot table, but with less information.
There's a couple of ways that I've thought about how to complete this, I personally think none of these are very good ideas but we'll see.
Algorithm 1, all 50 states:
Create 50 string variables for each state, create 50 long variables for the counts
Loop through raw data sheet, if specific state found then increment appropriate count (this would require 50 if-else statements)
Output results
Overall..... terrible idea
Algorithm 2, flip-flop:
Don't create any variables
If a state is found in raw data sheet , look in output sheet to check if state has been found before
If state has been found before, increment cell adjacent by one
If state has not been found before, change next available blank cell to state initials and initialize cell adjacent to one
Go back to raw data sheet
Overall..... this could work, but I feel as if it would take forever, even with raw data sheets that aren't very big but it has the benefit of not wasting memory like the 50 states algorithm and less lines of code
On a side note, is it possible to access a workbook's (or worksheet's) cells without activating that workbook? I ask because it would make the second algorithm run much quicker.
Thank you,
Jesse Smothermon
A couple of point that will speed up your code:
You don't need to active workbooks, worksheets or ranges to access them
eg
DIM wb as workbook
DIM ws as worksheet
DIM rng as range
Set wb = Workbooks.OpenText(Filename:=filePath, Tab:=True) ' or Workbooks("BookName")
Set ws = wb.Sheets("SheetName")
Set rng = ws.UsedRange ' or ws.[A1:B2], or many other ways of specifying a range
You can now refer to the workbook/sheet/range like
rng.copy
for each cl in rng.cells
etc
Looping through cells is very slow. Much faster to copy the data to a variant array first, then loop through the array. Also, when creating a large amount of data on a sheet, better to create it in a variant array first then copy it to the sheet in one go.
DIM v As Variant
v = rng
eg if rng refers to a range 10 rows by 5 columns, v becomes an array of dim 1 to 10, 1 to 5. The 5 minutes you mention would probably be reduced to seconds at most
Sub CountStates()
Dim shtRaw As Excel.Worksheet
Dim r As Long, nr As Long
Dim dict As Object
Dim vals, t, k
Set dict = CreateObject("scripting.dictionary")
Set shtRaw = ThisWorkbook.Sheets("Raw")
vals = Range(shtRaw.Range("C2"), _
shtRaw.Cells(shtRaw.Rows.Count, "C").End(xlUp)).Value
nr = UBound(vals, 1)
For r = 1 To nr
t = Trim(vals(r, 1))
If Len(t) = 0 Then t = "Empty"
dict(t) = dict(t) + 1
Next r
For Each k In dict.keys
Debug.Print k, dict(k)
Next k
End Sub
I implemented my second algorithm to see how it would work. The code is below, I did leave out little details in the actual problem to try and be more clear and get to the core problem, sorry about that. With the code below I've added the other "parts".
Code:
' this number refers to the raw data sheet that has just been activated
totalRow = ActiveSheet.Range("A1").End(xlDown).Row
For iRow = 2 To totalRow
' These are specific to the company needs, refers to addresses
If (ActiveSheet.Cells(iRow, 2) = "BA") Then
badAddress = badAddress + 1
ElseIf (ActiveSheet.Cells(iRow, 2) = "C") Then
coverageNoListing = coverageNoListing + 1
ElseIf (ActiveSheet.Cells(iRow, 2) = "L") Then
activeListing = activeListing + 1
ElseIf (ActiveSheet.Cells(iRow, 2) = "NC") Then
noCoverageNoListing = noCoverageNoListing + 1
ElseIf (ActiveSheet.Cells(iRow, 2) = "NL") Then
inactiveListing = inactiveListing + 1
ElseIf (ActiveSheet.Cells(iRow, 2) = "") Then
noHit = noHit + 1
End If
' Algorithm beginning
' If the current cell (in state column) has something in it
If (ActiveSheet.Cells(iRow, 10) <> "") Then
' Save value into a string variable
tempState = ActiveSheet.Cells(iRow, 10)
' If this is also in a billable address make variable true
If (ActiveSheet.Cells(iRow, 2) = "C") Or (ActiveSheet.Cells(iRow, 2) = "L") Or (ActiveSheet.Cells(iRow, 2) = "NL") Then
boolStateBillable = True
End If
' Output sheet
BillableWorkbook.Activate
For tRow = 2 To endOfState
' If the current cell is the state
If (ActiveSheet.Cells(tRow, 9) = tempState) Then
' Get the current hit count of that state
tempStateTotal = ActiveSheet.Cells(tRow, 12)
' Increment the hit count by one
ActiveSheet.Cells(tRow, 12) = tempStateTotal + 1
' If the address was billable then increment billable count
If (boolStateBillable = True) Then
tempStateBillable = ActiveSheet.Cells(tRow, 11)
ActiveSheet.Cells(tRow, 11) = tempStateBillable + 1
End If
Exit For
' If the tempState is unique to the column
ElseIf (tRow = endOfState) Then
' Set state, totalCount
ActiveSheet.Cells(tRow - 1, 9) = tempState
ActiveSheet.Cells(tRow - 1, 12) = 1
' Increment the ending point of the column
endOfState = endOfState + 1
' If it's billable, indicate with number
If (boolStateBillable = True) Then
tempStateBillable = ActiveSheet.Cells(tRow - 1, 11)
ActiveSheet.Cells(tRow - 1, 11) = tempStateBillable + 1
End If
End If
Next
' Activate raw data workbook
TextFileWorkbook.Activate
' reset boolean
boolStateBillable = False
Next
I ran it once and it seems to have worked. The problem is that it took roughly five minutes or so, the original code takes 0.2 (rough guess). I think the only way to make the code perform quicker is to somehow be able to not activate the two workbooks over and over. This means that the answer is not complete but I will edit if I figure out the rest.
Note I will revisit pivot tables to see if I can do everything that I need to in them, as of now it looks like there are a couple of things that I won't be able to change but I'll check
Thank you,
Jesse Smothermon
I kept with the second algorithm. There is the dictionary option that I forgot but I'm still not very comfortable with how it works and I generally don't understand it quite yet. I played with the code for a bit and changed some thing up, it now works faster.
Code:
' In output workbook (separate sheet)
Sheets.Add.Name = "Temp_Text_File"
' Opens up raw data workbook (originally text file
Application.DisplayAlerts = False
Workbooks.OpenText Filename:=filePath, Tab:=True
Application.DisplayAlerts = True
Set TextFileWorkbook = ActiveWorkbook
totalRow = ActiveSheet.Range("A1").End(xlDown).Row
' Copy all contents of raw data workbook
Cells.Select
Selection.Copy
BillableWorkbook.Activate
' Paste raw data into "Temp_Text_File" sheet
Range("A1").Select
ActiveSheet.Paste
ActiveWorkbook.Sheets("Billable_PDF").Select
' Populate long variables
For iRow = 2 To totalRow
If (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "BA") Then
badAddress = badAddress + 1
ElseIf (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "C") Then
coverageNoListing = coverageNoListing + 1
ElseIf (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "L") Then
activeListing = activeListing + 1
ElseIf (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "NC") Then
noCoverageNoListing = noCoverageNoListing + 1
ElseIf (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "NL") Then
inactiveListing = inactiveListing + 1
ElseIf (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "") Then
noHit = noHit + 1
End If
If (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 10) <> "") Then
tempState = ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 10)
If (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "C") Or (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "L") Or (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "NL") Then
boolStateBillable = True
End If
'BillableWorkbook.Activate
For tRow = 2 To endOfState
If (ActiveSheet.Cells(tRow, 9) = tempState) Then
tempStateTotal = ActiveSheet.Cells(tRow, 12)
ActiveSheet.Cells(tRow, 12) = tempStateTotal + 1
If (boolStateBillable = True) Then
tempStateBillable = ActiveSheet.Cells(tRow, 11)
ActiveSheet.Cells(tRow, 11) = tempStateBillable + 1
End If
Exit For
ElseIf (tRow = endOfState) Then
ActiveSheet.Cells(tRow, 9) = tempState
ActiveSheet.Cells(tRow, 12) = 1
endOfState = endOfState + 1
If (boolStateBillable = True) Then
tempStateBillable = ActiveSheet.Cells(tRow, 11)
ActiveSheet.Cells(tRow, 11) = tempStateBillable + 1
End If
End If
Next
'stateOneTotal = stateOneTotal + 1
'If (ActiveSheet.Cells(iRow, 2) = "C") Or (ActiveSheet.Cells(iRow, 2) = "L") Or (ActiveSheet.Cells(iRow, 2) = "NL") Then
' stateOneBillable = stateOneBillable + 1
'End If
'ElseIf (ActiveSheet.Cells(iRow, 10) = "FL") Then
'stateTwoTotal = stateTwoTotal + 1
'If (ActiveSheet.Cells(iRow, 2) = "C") Or (ActiveSheet.Cells(iRow, 2) = "L") Or (ActiveSheet.Cells(iRow, 2) = "NL") Then
' stateTwoBillable = stateTwoBillable + 1
'End If
End If
'TextFileWorkbook.Activate
If (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "C") Or (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "L") Or (ActiveWorkbook.Sheets("Temp_Text_File").Cells(iRow, 2) = "NL") Then
billableCount = billableCount + 1
End If
boolStateBillable = False
Next
' Close raw data workbook and raw data worksheet
Application.DisplayAlerts = False
TextFileWorkbook.Close
ActiveWorkbook.Sheets("Temp_Text_File").Delete
Application.DisplayAlerts = True
Thank you for the comments and suggestions. It is very much appreciated as always.
Jesse Smothermon

Resources