VBA Slow and doesn't work with longer sets of data - vba

Thanks for taking the time to look at this. I'm trying to figure out why this macro works with smaller lists of keywords but when we put in larger lists it doesn't work AND goes too slowly.
Program works like this:
Column A is the input: we paste keywords of interest
Column B is output 1: it populates with all the noise words or stop words that aren't of interest from A. (there will be a 2nd sheet where we can create a list of non-interest words like the, is, by, but, etc)
Column C is output 2: it populates with all the special characters from A.
This is what I have...works with smaller not with bigger sets of words. Been stumped for a while.
Option Explicit
Dim KeywordSearch As Range
Dim NoiseWords As Range
Dim cell As Range
Dim NoiseWord As Range
Dim i As Long , j As Long
Dim NWTable As ListObject
Dim NewRow As ListRow
Dim SCTable As ListObject
Sub Highlight()
Dim s As String
Dim offset As Integer
Dim word As String
Worksheets("Keyword Search").Activate
Set KeywordSearch = ActiveSheet.Range("B3", Range("B3").End(xlDown))
Set NWTable = ActiveSheet.ListObjects("Table1")
Set SCTable = ActiveSheet.ListObjects("SC")
Worksheets("Noise Words").Activate
Set NoiseWords = ActiveSheet.Range("B2", Range("B2").End(xlDown))
' clear table
On Error Resume Next
NWTable.DataBodyRange.ClearContents
Dim r As Range
Set r = NWTable.Range.Rows(1).Resize(2)
NWTable.Resize r
SCTable.DataBodyRange.ClearContents
Dim t As Range
Set t = SCTable.Range.Rows(1).Resize(2)
SCTable.Resize t
On Error GoTo 0
For Each cell In KeywordSearch
s = cell.Value
offset = 1
cell.Interior.Color = vbWhite
cell.Characters.Font.Color = vbBlack
Do
'Replace smart quotes
For j = 1 To Len(s)
cell.Characters(j, 1).Text = Replace(cell.Characters(j, 1).Text, Chr(147), """")
cell.Characters(j, 1).Text = Replace(cell.Characters(j, 1).Text, Chr(148), """")
' Find the special characters and add to SpecialCharacters list
If InStr("""!##$%&'+,.:;<=>?^`{|}~*()/", Mid(s, j, 1)) > 0 Then
cell.Characters(j, 1).Font.Color = vbRed
Set NewRow = SCTable.ListRows.Add
NewRow.Range.Cells(1, 1) = Mid(s, j, 1)
' Replace with spaces
Mid(s, j, 1) = " "
End If
Next
' Find the next space
i = InStr(offset, s, " ")
' If no spaces left then go to end
If i = 0 Then
i = Len(s) + 1
End If
' Extract the word
word = LCase(Mid(s, offset, i - offset))
' Capitalize AND OR NOT
If word = "and" Or word = "not" Or word = "or" Then
For j = 1 To Len(word)
cell.Characters(offset + j - 1, 1).Text = UCase(Mid(word, j, 1))
Next
End If
' Special case to capitalize w/
If word = "w/" And i < Len(s) Then
cell.Characters(i - 2, 2).Text = UCase(word)
End If
' Is the word in the NoiseWord list?
For Each NoiseWord In NoiseWords
If NoiseWord.Value = word Then
' Highlight word
cell.Characters(offset, i - offset).Font.Color = 5287936
' Add to NWList
Set NewRow = NWTable.ListRows.Add
NewRow.Range.Cells(1, 1) = word
Exit For
End If
Next
offset = i + 1
Loop Until i > Len(s)
Next
With NWTable.Sort
.SortFields.Clear
.SortFields.Add Key:=Range("Table1[[#All],[Noise Words]]"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.Apply
End With
NWTable.Range.RemoveDuplicates Columns:=1, Header:=xlYes
SCTable.Range.RemoveDuplicates Columns:=1, Header:=xlYes
Worksheets("Keyword Search").Activate
End Sub

Working with the Characters collection is pretty slow, so you may be stuck with some level of poor performance.
However, there are likely some points where you can shave off time.
E.g:
For j = 1 To Len(s)
cell.Characters(j, 1).Text = Replace(cell.Characters(j, 1).Text, Chr(147), """")
cell.Characters(j, 1).Text = Replace(cell.Characters(j, 1).Text, Chr(148), """")
You don't need to use the characters collection at all here: since you just cleared all of the font color, there's no need to use the Characters approach vs. just replacing via .Value
EDIT: it might be worth setting a flag within to loop to track whether any character-level formatting has been applied, so you can avoid any unneccessary use of .Characters and rely instead on .Value
You can remove this from the loop:
cell.Interior.Color = vbWhite
cell.Characters.Font.Color = vbBlack
and replace with
KeywordSearch.Interior.Color = vbWhite
KeywordSearch.Font.Color = vbBlack
before the loop
This
If word = "and" Or word = "not" Or word = "or" Then
For j = 1 To Len(word)
cell.Characters(offset + j - 1, 1).Text = UCase(Mid(word, j, 1))
Next
End If
could be faster as:
If word = "and" Or word = "not" Or word = "or" Then
cell.Characters(offset, len(word)).Text = UCase(word)
End If

To speed up calculation where you modify values in spreadsheet you need first to disable screen updates and reenable once you finish processing:
Disabling updates:
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Reenabling updates:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True

The problem is definitely with your use of the Characters collection. If you want to colour the different words found, fine, but do it after you manipulate all the string values.
Side Note: The repeated use of ActiveSheet scares me. Please set this to a variable at the start of the Subroutine and use the variable instead.
Dim Sheet as Worksheet
Set Sheet = ActiveSheet
.
Sheet.Range(...
.
Set Sheet = nothing
Look into reading/writing the data to a Variant instead of a range. (at least for the text manipulation) Here's an example of how to load a Range into a variant:
Dim vNoiseWords as Variant
vNoiseWords = Sheet.Range("B2", Sheet.Range("B2").End(xlDown)).Value2
Writing is just the opposite (but I usually have to transpose the array).
Then, you can go through the variant array and identify the text that needs to be coloured in the cell.
Minimize Any and All Interactions with the Sheet
...so limit any line that starts with ActiveSheet., Cell., Range. and only process it if it needs to be done.
Even Cell = UCase(Cell) is a huge waste of time.
Your much better off doing
Value = UCase(Cell.Value2)
If Value <> Cell.Value2 then Cell.Value2 = Value
Update
FYI, It's easy to pin point the slow parts of your code, by adding timestamps in between sections of code. Here is a simple routine that I use to keep track of time intervals and display the results in the immediate window.
Public Sub TimeStamp(Optional Prompt As String, Optional StartTimer As Boolean)
Static s_fTimer As Single, s_fIntervalTimer As Single
Dim fCurrTime As Single
fCurrTime = Timer
If StartTimer Then
s_fTimer = fCurrTime
s_fIntervalTimer = fCurrTime
End If
If Prompt <> vbNullString Then Prompt = " - " & Prompt
Debug.Print Format((fCurrTime - s_fTimer), "0.000s") & Format((fCurrTime - s_fIntervalTimer), "(0.000s)") & Prompt
s_fIntervalTimer = fCurrTime
End Sub
The first time you call it (or anytime you want to reset the total time counter), you should set the StartTimer = True like this:
TimeStamp "Start of Program", True
After that, just call the routine, with an optional prompt to keep track of the sections of code:
TimeStamp "After Smart Quote Loop"
TimeStamp "The End"
Then just look at the time intervals, find the largest ones and whittle them down if you don't think that they are reasonable. You'll find that every interaction with the UI/cells is the hold up, but processing data in the background takes very little time.

Related

Extract an alphanumeric from sentence

I would like to have an VBA to extract an alphanumeric value from a column G which is a sentence.
This sentence is generally a comment. So it includes characters and numbers.
The value always starts with AI0 and ends with 0. This can be 11 to 13 digits long. Sometimes the number is mentioned in the comment as AI038537500, also sometimes as AI038593790000.
I have researched through almost all the websites, but have not found any case like this. I know about the formulas, left, right, mid but in my case, it doesn't apply.
Any lead would be appreciable.
You may try something like this...
Place the following User Defined Function on a Standard Module and then use it on the sheet like
=GetAlphaNumericCode(A1)
UDF:
Function GetAlphaNumericCode(rng As Range)
Dim Num As Long
Dim RE As Object, Matches As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = False
.Pattern = "AI\d{9,}0"
End With
If RE.Test(rng.Value) Then
Set Matches = RE.Execute(rng.Value)
GetAlphaNumericCode = Matches(0)
Else
GetAlphaNumericCode = "-"
End If
End Function
Why not give something like the following a try?
Sub findMatches()
Dim strLength As Integer
Dim i As Long
For i = 1 To Rows.Count
Dim AllWords As Variant
AllWords = Split(Cells(i, 7).Value, " ")
For Each Item In AllWords
strLength = Len(Item)
If strLength > 0 And strLength <= 13 And Item Like "A10*?#" Then
Cells(i, 8) = Item
End If
Next
Next i
End Sub
Test Cases:
I am apple and my batch number is: A10545440 so incase you needed to know
Result: A10545440
Some random comment… A20548650
Result: NO RESULT
A101234567891 is an awesome alphanumeric combo
Result: A101234567891
Another random comment… A10555
Result: A10555
Notice: The above example covers cases where the alphanumeric combo, starting with A10 is either:
In the middle of a sentence, or
Beginning of a sentence, or
At the end of a sentence
Also note: right now it is set to go through ALL the rows... so if you want to limit that, change the Rows.Count in the For statement to whatever your set limit is.
EDIT:
In the above code, I am explicitly asking it to look in column G
can you give this a try? I think it should do the job, also you should ammend the code with the column values, I tested it with the comments being in column C, while the code will be written in column D.
Option Explicit
Sub FindValue()
Dim i As Long
Dim lastrow As Long
Dim lFirstChr As Long
Dim lLastChr As Long
Dim CodeName As String
lastrow = activesheet.Range("c" & Rows.Count).End(xlUp).Row
' gets the last row with data in it
For i = 1 To lastrow
' shuffles through all cell in data
lFirstChr = InStr(1, Cells(i, 3), "A10") ' gets the coordinate of the first instance of "A10"
If lFirstChr = 0 Then GoTo NextIteration
lLastChr = InStr(lFirstChr, Cells(i, 3), " ") ' gets the coordinate of the first instansce of space after "A10"
If lLastChr = 0 Then 'if there is no space after A10 then sets lastchr to the lenght of the string
lLastChr = Len(Cells(i, 3))
End If
CodeName = Mid(Cells(i, 3).Value, lFirstChr, lLastChr - lFirstChr) ' extracts the codename from the string value
Range("d" & i).Value = CodeName
Goto NextTteration
NextIteration:
Next i
End Sub

finding the lowest value in a cell Excel VBA

I am new to this. I am trying to find the lowest value in a cell with multiple values inside. For example,
48
44.50
41.00
37.50
I am trying to find 37.50. What should be the code for it?
Thanks
Based on your posted example:
Sub FindMin()
Dim s As String, CH As String
Dim wf As WorksheetFunction
Dim bry() As Double
Set wf = Application.WorksheetFunction
s = ActiveCell.Text
CH = Chr(10)
ary = Split(s, CH)
ReDim bry(LBound(ary) To UBound(ary))
For i = LBound(ary) To UBound(ary)
bry(i) = CDbl(ary(i))
Next i
MsgBox wf.Min(bry)
End Sub
This assumes that there is a hard return (ASCII-10) between the fields in the cell.
EDIT#1:
To make it into a function, remove the sub and replace with:
Public Function FindMin(r As Range) As Variant
Dim s As String, CH As String
Dim wf As WorksheetFunction
Dim bry() As Double
Set wf = Application.WorksheetFunction
s = r.Text
CH = Chr(10)
ary = Split(s, CH)
ReDim bry(LBound(ary) To UBound(ary))
For i = LBound(ary) To UBound(ary)
bry(i) = CDbl(ary(i))
Next i
FindMin = wf.Min(bry)
End Function
EDIT#2:
based on your comment, here is an example of input vs output:
Note that all the values are in a single cell and the values are separated by hard returns rather than spaces.
By code with same cell and a " " as delimiter to break
temp = Range("A1").Value
temp = Split(temp, " ")
Low = CInt(temp(0))
For i = 0 To UBound(temp) - 1
If CInt(temp(i)) < Low Then Low = CInt(temp(i))
Next
Range("a2").Value = Low
if they are in a range you can use a formula
=MIN(A1:A4)
This question is pretty close to one previously asked:
VBA/EXCEL: extract numbers from one cell that contained multiple values with comma
If you take the code from that answer and replace the comma with whatever is separating your values, you will be able to get access to them in VBA. Then you can write code to find the minimum.
You can make a macro to split the values for each cell you selected and then check for the highest value. And a quick check to make sure you are not parsing all the empty rows (when you selected a column).
The macro below will set the highest value in the next column.
Sub lowest()
Dim Values As Variant
Dim LowestValue As Double
Dim a As Range
Set a = Selection
For Each Row In a.Rows
For Each Cell In Row.Cells
LowestValue = -1
Values = Split(Cell.Value, Chr(10))
For Each Value In Values
If LowestValue = -1 Then
LowestValue = Value
ElseIf Value < LowestValue Then
LowestValue = Value
End If
Next
Cells(Cell.Row, Cell.Column + 1).Value = LowestValue
If IsEmpty(Cell.Value) Then GoTo EndLoop
Next Cell
Next Row
EndLoop:
End Sub

Excel VBA - Looking for ways to simplify loop

I recently made a loop that takes the string in each cell, searches for a "_" in the string, and if there is one cuts off that bit and any character after it. Looking at the code I realized it might be too elaborate and could be shortened or simplified, but I'm not quite sure how to do so. Is there a way to make this bit of code more efficient?
Sub Name_Change()
Sheets("Sheet1").Activate
Dim tg_row As Integer
tg_row = 1
For Each nm_cl In Range("Table1[Name]")
If InStr(1, nm_cl, "_", vbTextCompare) = 0 Then
Range("Table1[Name]").Cells(tg_row, 1).Value = nm_cl.Value
Else
Range("Table1[Name]").Cells(tg_row, 1) = _
Left(nm_cl, InStr(1, nm_cl, "_", vbTextCompare) - 1)
End If
tg_row = tg_row + 1
Next nm_cl
End Sub
Thank you for your help!
A first attempt at optimizing this would be to note that you are calling InStr multiple times. You can speed things up by computing it once, and storing the result.
Along with that I would note that presumably Range("Table1[Name]") only has one column (otherwise you would be overwriting the first column with data from the other columns). So, you can replace Range("Table1[Name]").Cells(tg_row, 1) with nm_cl. In doing this, we notice the redundant statement of nm_cl.Value = nm_cl.Value can be removed. This leads to the following code:
Sub Name_Change()
Sheets("Sheet1").Activate
Dim index As Long
For Each nm_cl In Range("Table1[Name]")
index = InStr(1, nm_cl, "_", vbTextCompare)
If index <> 0 Then
nm_cl = Left(nm_cl, index - 1)
End If
Next nm_cl
End Sub
If you need more efficiency, beyond this, you can load your data into a variant by using
dim data as Variant
data = Range("Table1[Name]").Value
process all of your data within VBA, and then put it back to the worksheet using
Range("Table1[Name]").Value = data
This will increase your speed, as transfering data between Excel and VBA is slow and this means you will have 1 read and 1 write, instead of 1 read and 1 write per line, but it will require a (minor) rewrite of your algorithm as the syntax for working with an array within a variant is different from working with ranges. Note that this will not work if you go beyond the 65536 rows. I beleive that it is a legacy constraint from Excel 2003 and earlier.
You could adjust your loop to only modify the cells that contain "_".
If Not InStr(1, nm_cl, "_", vbTextCompare) = 0 Then
Range("Table1[Name]").Cells(tg_row, 1) = _
Left(nm_cl, InStr(1, nm_cl, "_", vbTextCompare) - 1)
End If
EDIT:
Here's a working example that includes #Degustaf's suggestions. Just change the name of the range to fit your worksheet.
Sub Name_Change()
Dim selectedRange As Range
Dim rangeData As Variant 'Array containing data from specified range
Dim col As Long 'Selected column from range
Dim row As Long 'Selected row from range
Dim cellValue As String 'Value of selected cell
Dim charPosition As Long 'Position of underscore
Sheets("Sheet1").Activate
Set selectedRange = Range("YOUR-NAMED-RANGE-HERE")
If selectedRange.Columns.Count > 65536 Then
MsgBox "Too many columns!", vbCritical
ElseIf selectedRange.Rows.Count > 65536 Then
MsgBox "Too many rows!", vbCritical
Else
rangeData = selectedRange.Value
If UBound(rangeData, 1) > 0 And UBound(rangeData, 2) > 0 Then
'Iterate through rows
For row = 1 To UBound(rangeData, 1)
'Iterate through columns
For col = 1 To UBound(rangeData, 2)
'Get value of cell
cellValue = CStr(rangeData(row, col))
'Get position of underscore
charPosition = InStr(1, cellValue, "_", vbTextCompare)
'Update cell data stored in array if underscore exists
If charPosition <> 0 Then
rangeData(row, col) = Left(cellValue, charPosition - 1)
End If
Next col
Next row
'Overwrite range with array data
selectedRange.Value = rangeData
End If
End If
End Sub
You could use a user defined function to return the truncated strings in cells.
The Worksheet-function could look like:
Public function truncateAt( s as String) as string
dim pos as integer
pos = instr (1, s,"_")
If pos> 0 then
truncateAt= left (s, pos)
Else
truncateAt= s
End If
End function

Moving Data and Refencing Sheet Object

I am trying to automate a spreadsheet to transfer data from one sheet to another sheet depending on what the first 3 characters of the data is. So for example, for the data NDX 12/31/2012 P2600, I would like it to be placed in the NDX sheet. So I have an array (desArr()) that splits that data into different positions of the array, such that desArr(0) contains "NDX", desArr(1) contains "12/31/2012" and so on.
The part I am having trouble with is moving the data to the respective sheets. Specifically, I need a variable reference to these spreadsheets. For instant, take the NDX sheet. I know I can just do NDX.cells(1,1).Paste or Worksheets(NDX.Name).Cells(1,1).Paste and that would work, but what if I want to do that for multiple sheets? I could obviously use If statements to define each different instance, but I wanted to shorten my code. Hence, I am trying to make the reference to the sheet objects variable, i.e. desArr(0).Name, but it returns with an error (which I understand why). Anyone with suggestions on how to achieve this? I know one solution is to just use the name property of the worksheet, but I wanted to avoid the chance of my code failing if someone changed the name of the sheets.
So perhaps like:
Dim desArr() As String, desInfo As String, opType As String
Dim rNum As Long, cNum As Long, i As Long
Dim wb As Workbook
Dim ws As Worksheet
Dim sortRng As Range, findRng As Range
Dim j As Integer 'Throw away after testing
Dim test As String 'Throw away after testing
Dim k As Integer 'Throw away after testing
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Set wb = ThisWorkbook
Set ws = wb.Worksheets(Import.Name)
With ws
rNum = .Range("C1048576").End(xlUp).Row
cNum = 6 'Number of used columns starting from left
Set sortRng = .Range(.Cells(3, 2), .Cells(rNum, cNum))
'Sort range according to Type and Description
sortRng.Sort _
Key1:=.Range("B1"), _
Key2:=.Range("C1")
'Apply conditional formatting
With sortRng.Columns(2)
.FormatConditions.AddUniqueValues
.FormatConditions(.FormatConditions.Count).SetFirstPriority
.FormatConditions(1).DupeUnique = xlDuplicate
With sortRng.Columns(2).FormatConditions(1)
.Interior.PatternColorIndex = xlAutomatic
.Interior.Color = 13551615
.Interior.TintAndShade = 0
.StopIfTrue = False
End With
End With
For i = 0 To (rNum - 2)
With sortRng.Cells(i + 1, 2)
If .DisplayFormat.Interior.Color = "13551615" Then
j = 0
While (.Value = .Offset(j + 1, 0).Value And .Offset(0, 1).Value = .Offset(j + 1, 1).Value)
j = j + 1
Wend
If (j <> 0) Then 'There are duplicates
End If
End If
'Converting the description to format used for classification
If .Offset(0, -1) = "Ext Option" Then
desArr = Split(.Value, " ")
If Not (Left(.Value, 3) = "SX5" Or Left(.Value, 3) = "UKX") Then
'check if it's a call or put
If Left(desArr(3), 1) = "C" Then
opType = "Call"
ElseIf Left(desArr(3), 1) = "P" Then
opType = "Put"
Else
opType = "N/A"
End If
desInfo = Format(desArr(2), "mmmdd") & " " & Right(Trim(desArr(3)), Len(Trim(desArr(3))) - 1) & " " & opType
Else
'check if it's a call or put
If Left(desArr(2), 1) = "C" Then
opType = "Call"
ElseIf Left(desArr(2), 1) = "P" Then
opType = "Put"
Else
opType = "N/A"
End If
desInfo = Format(desArr(1), "mmmdd") & " " & Right(Trim(desArr(2)), Len(Trim(desArr(2))) - 1) & " " & opType
End If
End If
End With
Next i
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Except that NDX would have to be variable as which worksheet to move the data to depends on the data.
You can use the codename property of the worksheets. If you use NDX.Cells(1,1), NDX is the codename of the sheet. simply search all worksheets, e.g.:
Function GetWorksheet(byval withCodename as String) as Worksheet
Dim sheetVar as Worksheet
For each sheetVar in ThisWorkbook.Worksheets
If sheetVar.CodeName = withCodename Then
Set GetWorksheet = sheetVar
End if
Next
End Function
You could:
Prevent user from renaming sheets
You wrote: "I wanted to avoid the chance of my code failing if someone changed the name of the sheets."
Well, the user can't do this:
If you protect the workbook. You can do this manually in the ribbon (Review > Changes > Protect workbook), or programmatically like this:
ThisWorkbook.Protect 'optionally, add a password -- see documentation for Protect
This will entirely prevent the user from changing sheet names.

Big loop crashes in VBA

Screenshot
I am updating a word list (2) with the frequency ranking of another list (1). The code is designed to for every entry in list 1 go through list 2 and add the frequency ranking to every identical entry in it. If I limit the list to a few entries in each, it works exactly as intended, but the lists are quite large. List 1 contains 55.000 words and list 2 contains 18.000 words. Is there a way to prevent the code from crashing, or alternatively rewrite it in a more efficient manner? I am sure it is far from optimal, as I am a complete neophyte in VBA. I’ll paste in the code below.
Thanks much
Option Explicit
Sub CorrectFrequencyData()
Dim Frequency As Double
Dim CurrentLocation As Range
Application.ScreenUpdating = False
Set CurrentLocation = Range("i5")
Do Until CurrentLocation.Value = ""
Frequency = CurrentLocation.Offset(0, -6).Value
Range("n4").Activate
Do Until ActiveCell.Value = ""
If ActiveCell.Value = CurrentLocation.Value Then ActiveCell.Offset(0, 1).Value = ActiveCell.Offset(0, 1).Value + Frequency
ActiveCell.Offset(1, 0).Activate
Loop
Set CurrentLocation = CurrentLocation.Offset(1, 0)
Loop
Application.ScreenUpdating = True
End Sub
It Looks like there may be a few ways to speed up your code. Firstly you could use a SUMIF as GavinP suggested like so in your second frequency column =SUMIF(I:I, N4, C:C) If you flow this down for your second frequency column what this is saying is check column I for the value in N + row and everywhere that you find that value at the frequency from column C to a Total.
Now options to speed up your code:
Option Explicit
Sub CorrectFrequencyData()
Application.ScreenUpdating = False
I'm not sure if you have formulas in your code but you can set them to manual instead of having them recalculate every time you change values on your sheet.
Application.Calculation = -4135 'xlCalculationManual
Instead of looping through your sheet you can assign your range to an array and loop through that which is faster. We can also eliminate the need to loop through the second list for every entry in the first list. We'll do this by storing the first list of words and their frequency in a dictionary
Dim ArrWords() as variant
Dim LastRow as long
LastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 9).End(-4162).Row 'Version non-specific Endrow, xlUP
ArrWords = Range("C4:I" & LastRow)
Dim dicWordFrequency as Object
Set dicWordFrequency = CreateObject("Dictionary.Scripting")
Dim tempWord as String
Dim i as Long
For i = 1 to Ubound(ArrWords)
tempWord = arrWords(i,7)
If not dicWordFrequency.Exists(tempWord) then
DicWordFrequency.Add tempWord, arrWords(i,1)
Else
DicWordFrequency.Item(tempWord)= dicWordFrequency.Item(tempWord) + arrWords(i,1)
End If
Next
Now we can loop through your worksheet and update the frequencies for the words in the second list.
LastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 14).End(-4162).Row 'Version non-specific Endrow, xlUP
ArrWords = Range("N4:O" & LastRow)
For i = 1 to Ubound(arrWords)
tempWord = arrwords(i,1)
If dicWordFrequency.Exists(tempWord) then
arrWords(i,2) = dicWordFrequency.Item(tempWord)
End If
Next
'Dump your new array with the totals to a range
Dim result as Range
Set Result = Range("N4")
Result.ReSize(UBound(arrWords,1), Ubound(ArrWords,2)).value = arrWords
Application.ScreenUpdating = True
Application.Calculation = -4105 'xlCalculationAutomatic
End Sub