For Loop VBA Speed up by putting into an array - vba

I am trying to go through about 2000 lines worth of data and do a for loop and several if statements within each loop. It works, but right now it is painfully slow. I understand from my research that if I can put the data into an array and manipulate it there and then put the data back into the cells would be much faster but I could use some help on the coding to do so. Here is my code.
Sub EliminateVariance()
Dim Old As Long
Dim Older As Long
Dim Oldest As Long
Dim Current As Long
Dim VarianceOld As Long
Dim VarianceNew As Long
Dim VarianceNew1 As Long
Dim VarianceNew2 As Long
Dim Month1 As Variant
Dim SheetName As Variant
Dim LastRow As Long
Dim i As Long
Month1 = InputBox("What is this month?")
SheetName = Month1 & " SummaryByCust"
Worksheets(SheetName).Activate
LastRow = Cells(Rows.count, "B").End(xlUp).row
For i = 3 To LastRow
VarianceOld = Range("V" & i)
Oldest = Range("I" & i)
Older = Range("H" & i)
Old = Range("G" & i)
Current = Range("F" & i)
If VarianceOld > Oldest Then
VarianceNew = VarianceOld - Oldest
Range("I" & i) = 0
If VarianceNew > Older Then
VarianceNew1 = VarianceNew - Older
Range("H" & i) = 0
If VarianceNew1 > Old Then
VarianceNew2 = VarianceNew1 - Old
Range("G" & i) = 0
If VarianceNew2 > Current Then
MsgBox ("Error: Deferred is greater than what it should be. Verify your numbers")
Else
Range("F" & i) = Current - VarianceNew2
End If
Else
Range("G" & i) = Old - VarianceNew1
End If
Else
Range("H" & i) = Older - VarianceNew
End If
Else
Range("I" & i) = Oldest - VarianceOld
End If
Next i
End Sub

Here is an example on how to use Arrays:
Sub arrayEx()
'Set the range
Dim rng As Range
Set rng = Worksheets("Sheet1").Range("A1:B20000")
'Bulk load the values from the range into an array
'Even if a single column this will create a 2D array
Dim rngArr As Variant
rngArr = rng.Value
'Loop the "Rows" of the array
Dim i As Long
For i = LBound(rngArr, 1) To UBound(rngArr, 1)
'Do something with that array
'when loaded from a range it is similar nomenclature to Cells: array(row,column)
If rngArr(i, 1) = "A" Then
rngArr(i, 2) = "B"
End If
Next i
'overwrite the values in range with the new values from the array.
rng.Value = rngArr
End Sub
Try to adapt to fit your needs.

Related

Matching Multiple Criteria and Returning Multiple Values

I have two spreadsheets (wb and wbtemp); both have a column for location and a column for feature type. In VBA, I want to find all of the rows on the second sheet where the two columns are the same as the two columns on a row in the first sheet and get a list or a range made up of the row numbers/indices.
I then want to use this range to pull out values from a different column and find the highest object in it, but I think I will probably be able to do that if I can get this range sorted.
Dim wb As Workbook
Dim ws As Worksheet
Dim Features() As Variant
Dim Activity() As Variant
Dim Benthic As Variant
Dim wbtemp As Workbook
Dim BenSenFeatures() As Variant
Dim BenSenActivity() As Variant
Dim LR As Long
Dim LC As Long
Dim r As Long
Dim c As Long
Dim WhatToFind1 As Variant
Dim WhatToFind2 As Variant
Dim rngFound1 As Range
Dim rngFound2 As Range
Dim rng1 As Variant
Dim rng2 As Variant
Dim rngFound As Range
Dim iLoop As Long
Dim colFound As Range
Set wb = ActiveWorkbook
Set ws = wb.ActiveSheet
Features = ws.Range("B:C").Value
Activity = ws.Rows(1).Value
Benthic = InputBox("Filename goes here...")
Set wbtemp = Workbooks.Open(Benthic, True, True)
With wbtemp
BenSenFeatures = .Sheets(1).Range("A:B").Value
BenSenActivity = .Sheets(1).Rows(1).Value
End With
LR = ws.Range("C" & Rows.Count).End(xlUp).Row
LC = ws.Cells(1, Columns.Count).End(xlToLeft).Column
For r = 3 To LR
If Not IsEmpty(Features(r, 2)) Then
If IsInArray(Features(r, 2), BenSenFeatures, 2) Then
'If WorksheetFunction.Match(Features(r, 2), BenSenFeatures(0, 2), 0) Then <---I tried to use the arrays originally
WhatToFind1 = Features(r, 1)
WhatToFind2 = Features(r, 2)
Set rngFound1 = wbtemp.Sheets(1).Columns(1).Cells(wbtemp.Sheets(1).Columns(1).Cells.Count)
Set rngFound2 = wbtemp.Sheets(1).Columns(2).Cells(wbtemp.Sheets(1).Columns(2).Cells.Count)
For iLoop = 1 To WorksheetFunction.CountIf(wbtemp.Sheets(1).Columns(1), WhatToFind1)
Set rngFound1 = wbtemp.Sheets(1).Columns(1).Cells.Find(WhatToFind1, After:=rngFound1)
rng1(iLoop) = rngFound1.Row
'WorksheetFunction.Index(wbtemp.Sheets(1).Range("A:B").Value,_
WorksheetFunction.Match(WhatToFind1 & WhatToFind2,_
wbtemp.Sheets(1).Columns(1) & wbtemp.Sheets(1).Columns(2),_
0), 1) <---originally tried to use match to search for the multiple criteria but couldn't find a way to create a list of indices
Set rngFound2 = wbtemp.Sheets(1).Columns(2).Cells.Find(WhatToFind2, After:=rngFound2)
rng2(iLoop) = rngFound2.Row
Next iLoop
For Each cell In rng1
If Not Application.CountIf(rng2, cell.Value) = 0 Then
rngFound.Cells(Cells(Rows.Count, 1).End(xlUp) + 1) = cell.Value
End If
Next
I originally tried to use .Match to find the multiple criteria, but I couldn't figure out how to create a range of indices from it. Then I tried using .Find to create two list of indices but I can't figure out how to get that to work. I keep getting
Type Mismatch
errors.
I realise this sounds confusing, so let me know if anything needs clarifying.
Something like this should work for you. I tried to comment the code for clarity.
Sub tgr()
Dim wb As Workbook
Dim ws As Worksheet
Dim rData As Range
Dim wbTemp As Workbook
Dim wsTemp As Worksheet
Dim rTempData As Range
Dim aData() As Variant
Dim aTempData() As Variant
Dim aResults() As Variant
Dim lNumResults As Long
Dim DataIndex As Long, TempIndex As Long, ResultIndex As Long, j As Long
Dim sCritRange1 As String, sCritRange2 As String
Dim sCriteria1 As String, sCriteria2 As String
Set wb = ActiveWorkbook
'Adjust these two as necessary
Set ws = wb.Sheets(1)
Set rData = ws.Range("B3", ws.Cells(ws.Rows.Count, "B").End(xlUp))
'Select wbTemp file
On Error Resume Next
Set wbTemp = Workbooks.Open(Application.GetOpenFilename("Excel Files, *.xls*"))
On Error GoTo 0
If wbTemp Is Nothing Then Exit Sub 'Pressed cancel
'Adjust these two as necessary
Set wsTemp = wbTemp.Sheets(1)
Set rTempData = wsTemp.Range("A1", wsTemp.Cells(wsTemp.Rows.Count, "A").End(xlUp))
sCritRange1 = rTempData.EntireColumn.Address(external:=True)
sCritRange2 = rTempData.Offset(, 1).EntireColumn.Address(external:=True)
sCriteria1 = rData.Address(external:=True)
sCriteria2 = rData.Offset(, 1).Address(external:=True)
lNumResults = Evaluate("SUMPRODUCT(COUNTIFS(" & sCritRange1 & "," & sCriteria1 & "," & sCritRange2 & "," & sCriteria2 & "))")
If lNumResults = 0 Then Exit Sub 'No matches
ReDim aResults(1 To lNumResults, 1 To 3)
aData = rData.Resize(, 2).Value
aTempData = rTempData.Resize(, 2).Value
'Loop through both data ranges
For DataIndex = LBound(aData, 1) To UBound(aData, 1)
For TempIndex = LBound(aTempData, 1) To UBound(aTempData, 1)
'Find where both criteria matches
If aTempData(TempIndex, 1) = aData(DataIndex, 1) And aTempData(TempIndex, 2) = aData(DataIndex, 2) Then
'Match found, add to results and collect the row index
ResultIndex = ResultIndex + 1
aResults(ResultIndex, 1) = aData(DataIndex, 1)
aResults(ResultIndex, 2) = aData(DataIndex, 2)
aResults(ResultIndex, 3) = "Row: " & TempIndex + rTempData.Row - 1 'This is the row index from wsTemp of the found match
End If
Next TempIndex
Next DataIndex
'Row index results gathered
'Do what you want with the results
'In this example it is just providing msgboxes displaying the results
For ResultIndex = LBound(aResults, 1) To UBound(aResults, 1)
MsgBox "Location: " & aResults(ResultIndex, 1) & Chr(10) & _
"Feature: " & aResults(ResultIndex, 2) & Chr(10) & _
"RowIndex: " & aResults(ResultIndex, 3)
Next ResultIndex
'Close wbTemp
wbTemp.Close
End Sub
I made some minor modifications to tigeravatar's answer to get it to work with my data:
Mainly creating a loop which cycled through each row in wb so that the criteria used with CountIfs was a single value and not a range of values.
I swapped the Evaluate("SUMPRODUCT(COUNTIFS(" & sCritRange1 & "," & sCriteria1 & "," & sCritRange2 & "," & sCriteria2 & "))") for Application.WorksheetFunction.CountIfs(Range(sCritRange1), Range(sCriteria1).Value, Range(sCritRange2), Range(sCriteria2).Value)
I would like to thank tigeravatar for their help.
LR = ws.Range("C" & Rows.Count).End(xlUp).Row
LC = ws.Cells(1, Columns.Count).End(xlToLeft).Column
For r = 3 To LR
sCritRange1 = rTempData.EntireColumn.Address(external:=True)
sCritRange2 = rTempData.Offset(, 1).EntireColumn.Address(external:=True)
sCriteria1 = rData(r, 1).Address(external:=True)
sCriteria2 = rData(r, 1).Offset(, 1).Address(external:=True)
lNumResults = Application.WorksheetFunction.CountIfs(Range(sCritRange1), Range(sCriteria1).Value, Range(sCritRange2), Range(sCriteria2).Value)
If lNumResults = 0 Then Exit Sub 'No matches
ReDim aResults(1 To lNumResults, 1 To 3)
aData = rData(r, 1).Resize(, 2).Value
aTempData = rTempData.Resize(, 2).Value
'Loop through both data ranges
For DataIndex = LBound(aData, 1) To UBound(aData, 1)
For TempIndex = LBound(aTempData, 1) To UBound(aTempData, 1)
'Find where both criteria matches
If Not IsEmpty(aTempData(TempIndex, 1)) Then
If aTempData(TempIndex, 1) = aData(DataIndex, 1) And aTempData(TempIndex, 2) = aData(DataIndex, 2) Then
'Match found, add to results and collect the row index
ResultIndex = ResultIndex + 1
aResults(ResultIndex, 1) = aData(DataIndex, 1)
aResults(ResultIndex, 2) = aData(DataIndex, 2)
aResults(ResultIndex, 3) = "Row: " & TempIndex + rTempData.Row - 1 'This is the row index from wsTemp of the found match
End If
End If
Next TempIndex
Next DataIndex
Next r

Combining consecutive values in a column with the help of VBA

I have a data like this :
A049
A050
A051
A053
A054
A055
A056
A062
A064
A065
A066
And I want the output like :
As you can see, I want the ranges which are in consecutive order
I am trying some thing like this:
Private Sub CommandButton1_Click()
Set wb = ThisWorkbook
lastRow = wb.Sheets("Sheet1").Range("A" & wb.Sheets("Sheet1").Rows.Count).End(xlUp).Row
For i = 2 To lastRow
r = wb.Sheets("Sheet1").Range("A" & i).Value
If wb.Sheets("Sheet1").Range("A" & i).Value = wb.Sheets("Sheet1").Range("A" & i+1).Value
Next i
End Sub
But not helping me
Am feeling charitable so have tried some code which should work. It assumes your starting values are in A1 down and puts results in C1 down.
Sub x()
Dim v1, v2(), i As Long, j As Long
v1 = Range("A1", Range("A" & Rows.Count).End(xlUp)).Value
ReDim v2(1 To UBound(v1, 1), 1 To 2)
For i = LBound(v1, 1) To UBound(v1, 1)
j = j + 1
v2(j, 1) = v1(i, 1)
If i <> UBound(v1, 1) Then
Do While Val(Right(v1(i + 1, 1), 3)) = Val(Right(v1(i, 1), 3)) + 1
i = i + 1
If i = UBound(v1, 1) Then
v2(j, 2) = v1(i, 1)
Exit Do
End If
Loop
End If
If v1(i, 1) <> v2(j, 1) Then v2(j, 2) = v1(i, 1)
Next i
Range("C1").Resize(j, 2) = v2
End Sub
Try the below code
Private Sub CommandButton1_Click()
Set wb = ThisWorkbook
lastRow = wb.Sheets("Sheet1").Range("A" & wb.Sheets("Sheet1").Rows.Count).End(xlUp).Row
Dim lastNum, Binsert As Integer
Dim firstCell, lastCell, currentCell As String
Binsert = 1
lastNum = getNum(wb.Sheets("Sheet1").Range("A1").Value)
firstCell = wb.Sheets("Sheet1").Range("A1").Value
For i = 2 To lastRow
activeNum = getNum(wb.Sheets("Sheet1").Range("A" & i).Value)
currentCell = wb.Sheets("Sheet1").Range("A" & i).Value
If (activeNum - lastNum) = 1 Then
'nothing
Else
lastCell = wb.Sheets("Sheet1").Range("A" & (i - 1)).Value
wb.Sheets("Sheet1").Range("B" & Binsert).FormulaR1C1() = firstCell
If (firstCell <> lastCell) Then
wb.Sheets("Sheet1").Range("C" & Binsert).FormulaR1C1() = lastCell
End If
Binsert = Binsert + 1
firstCell = wb.Sheets("Sheet1").Range("A" & i).Value
End If
lastNum = activeNum
Next i
'last entry
wb.Sheets("Sheet1").Range("B" & Binsert).FormulaR1C1() = firstCell
If (firstCell <> currentCell) Then
wb.Sheets("Sheet1").Range("C" & Binsert).FormulaR1C1() = currentCell
End If
End Sub
Public Function getNum(ByVal num As String) As Integer
getNum = Val(Mid(num, 2))
End Function
Another solution. It loops backwards from last row to first row.
Option Explicit
Public Sub FindConsecutiveValues()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim lRow As Long 'find last row
lRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Dim lVal As String 'remember last value (stop value)
lVal = ws.Range("A" & lRow).Value
Const fRow As Long = 2 'define first data row
Dim i As Long
For i = lRow To fRow Step -1 'loop from last row to first row backwards
Dim iVal As Long
iVal = Val(Right(ws.Range("A" & i).Value, Len(ws.Range("A" & i).Value) - 1)) 'get value of row i without A so we can calculate
Dim bVal As Long
bVal = 0 'reset value
If i <> fRow Then 'if we are on the first row there is no value before
bVal = Val(Right(ws.Range("A" & i - 1).Value, Len(ws.Range("A" & i - 1).Value) - 1)) 'get value of row i-1 without A
End If
If iVal - 1 = bVal Then
ws.Rows(i).Delete 'delete current row
Else
If lVal <> ws.Range("A" & i).Value Then 'if start and stop value are not the same …
ws.Range("B" & i).Value = lVal 'write stop value in column B
End If
lVal = ws.Range("A" & i - 1).Value 'remember now stop value
End If
Next i
End Sub

Convert headers and row level data to column level

I have very little experience working with VBA, so I'm having a hard time looking up what I am trying to do because I am having a hard time putting what I am trying to do into words.
I have been struggling to write a code to do the below task for the past few days.
Basically what I am trying to do is to convert a set of data to different format.
This what my source data looks like.
Data:
and I need it to look like this
FinalLook:
I've a already setup a code which is lengthy and incomplete.
FIRST PART
I started with retrieving a part of a data (AQ:BA) and then convert to the format in sheet2 with the below code.
Sub FirstPart()
Dim lastRow As Long
Dim Laaastrow As Long
Sheets("sheet2").Range("a2:A5000").ClearContents
lastRow = Sheets("Sheet1").Range("c" & Rows.Count).End(xlUp).Row
Sheets("Sheet2").Range("A2:A" & lastRow).Value = Sheets("Sheet1").Range("c5:c" & lastRow).Value
Sheets("Sheet2").Range("b2:l" & lastRow).Value = Sheets("Sheet1").Range("aq5:ba" & lastRow).Value
End Sub
But.. the problem i am facing with this code is that it pulls all the data, i do not want it to pull all the values, but only the ones which is not empty or 0. In other words, if AQ6:BA6 is empty, script should skip this particular row and go the next one.
SECOND PART (converting the sheet2 data to the final format)
Sub NormalizeSheet()
Dim wsSheet2 As Worksheet
Dim wsSheet4 As Worksheet
Dim strKey As String
Dim clnHeader As Collection
Dim lngColumnCounter As Long
Dim lngRowCounterSheet2 As Long
Dim lngRowCounterSheet4 As Long
Dim rngCurrent As Range
Dim varColumn As Variant
Set wsSheet2 = ThisWorkbook.Worksheets("Sheet2")
Set wsSheet4 = ThisWorkbook.Worksheets("Sheet4")
Set clnHeader = New Collection
wsSheet4.Range("c2:c5000").ClearContents
wsSheet4.Range("e2:e5000").ClearContents
wsSheet4.Range("g2:g5000").ClearContents
lngColumnCounter = 2
lngRowCounterSheet2 = 1
Set rngCurrent = wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter)
Do Until IsEmpty(rngCurrent.Value)
clnHeader.Add rngCurrent.Value, CStr(lngColumnCounter)
lngColumnCounter = lngColumnCounter + 1
Set rngCurrent = wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter)
Loop
lngRowCounterSheet2 = 2
lngRowCounterSheet4 = 1
lngColumnCounter = 1
Do While Not IsEmpty(wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter))
Set rngCurrent = wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter)
strKey = rngCurrent.Value
lngColumnCounter = 2
Do While Not IsEmpty(wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter))
Set rngCurrent = wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter)
If rngCurrent.Value = "NULL" Then
Else
wsSheet4.Range("c" & lngRowCounterSheet4).Offset(1, 0).Value = strKey
wsSheet4.Range("e" & lngRowCounterSheet4).Offset(1, 0).Value = clnHeader(CStr(lngColumnCounter))
wsSheet4.Range("g" & lngRowCounterSheet4).Offset(1, 0).Value = rngCurrent.Value
lngRowCounterSheet4 = lngRowCounterSheet4 + 1
End If
lngColumnCounter = lngColumnCounter + 1
Loop
lngRowCounterSheet2 = lngRowCounterSheet2 + 1
lngColumnCounter = 1
Loop
End Sub
I got this code from another thread posted here on stakcoverflow, i modified a bit to get this work.
The problem i am encountering here is that if Sheet2 B2 is empty, the codes doesnt check sheet C2 instead it skips the whole row, which is not right here.
I know this sounds complicated, and this approach of mine may not be even feasible.
Is there ANY OTHER WAY to do this? Is there any other way to get this in a single shot instead of breaking down the data and move each set of columns to sheet2 then to final format?
See how you get on with this. You'll have to adjust range references, and possibly sheet names
Sub x()
Dim r As Long, c As Range
With Sheet1
For r = 5 To .Range("A" & Rows.Count).End(xlUp).Row
For Each c In .Range(.Cells(r, "AQ"), .Cells(r, "BK")).SpecialCells(xlCellTypeConstants)
If c.Value > 0 Then
Sheet2.Range("A" & Rows.Count).End(xlUp)(2).Value = .Range("B1").Value
Sheet2.Range("B" & Rows.Count).End(xlUp)(2).Value = .Cells(r, 1).Value
Sheet2.Range("C" & Rows.Count).End(xlUp)(2).Value = .Cells(r, 2).Value
Sheet2.Range("D" & Rows.Count).End(xlUp)(2).Value = .Cells(3, c.Column).Value
Sheet2.Range("E" & Rows.Count).End(xlUp)(2).Value = .Cells(4, c.Column).Value
Sheet2.Range("F" & Rows.Count).End(xlUp)(2).Value = "(blank)"
Sheet2.Range("G" & Rows.Count).End(xlUp)(2).Value = c.Value
End If
Next c
Next r
End With
Sheet2.Range("A1").Resize(, 7) = Array("TOPHEADER", "HEADER1", "HEADER2", "FROM", "TO", "TYPE", "UNIT")
End Sub

MS Excel VBA - Match/Index trying to pull in dates to add to a total date duration

For DurcurrRowIn = 14 To .UsedRange.Row + .UsedRange.Rows.Count - 1
DurcurrRowIn = DurcurrRowIn + 1
Set DurlookFor = wb.ActiveSheet.Cells(currRowIn, "C") ' value to find
Set Durlookforin = wb.ActiveSheet.Range("C" & DurcurrRowIn & ":C500")
On Error Resume Next
DurStart = Application.WorksheetFunction.Index(wb.ActiveSheet.Range("F:F"), WorksheetFunction.Match(DurlookFor, Durlookforin, 0))
DurEnd = Application.WorksheetFunction.Index(wb.ActiveSheet.Range("G:G"), WorksheetFunction.Match(DurlookFor, Durlookforin, 0))
Dur1 = DurEnd - DurStart
Dur = Dur + Dur1
Next
Looking to perform an index/match to grab an employees id on the current row and look for it further down the column not including the row I'm currently in. Once it finds the same employee id, grab the start/end dates and determine their durations, then add that figure to the total duration number (dur). Currently not grabbing the dates, though it did work when my "Durlookforin" was just the whole column C, which doesn't really help. Will post my dimensions below.
Dim DurcurrRowIn As Long
Dim DurcurrColIn As Long
Dim DurcurrRowOut As Long
Dim wb As Workbook
Dim Dur As Long
Dim Dur1 As Long
Dim DurEnd As String
Dim DurStart As String
Dim DurlookFor As Range, Durlookforin As Range
Test File Here
Using Employee ID's as keys to a Dictionary makes it easy to add up the Durations.
Most of this code generates a report but this is all you need to get the Durations by Employee ID's
For x = 14 To lastRow
dicIDs(.Cells(x, "C").Value) = dicIDs(.Cells(x, "C").Value) + .Cells(x, "G").Value - .Cells(x, "F").Value
Next
Sample Data
Result
Sub GetDurations()
Application.ScreenUpdating = False
Const SOURCE_SHEETNAME As String = "Sheet1"
Dim lastRow As Long, x As Long
Dim dicIDs As Object
Set dicIDs = CreateObject("System.Collections.SortedList")
With Worksheets(SOURCE_SHEETNAME)
lastRow = .Range("C" & Rows.Count).End(xlUp).Row
For x = 14 To lastRow
dicIDs(.Cells(x, "C").Value) = dicIDs(.Cells(x, "C").Value) + .Cells(x, "G").Value - .Cells(x, "F").Value
Next
Worksheets.Add
.Range("A14:D" & lastRow).Copy Range("A2")
End With
Columns("D").ClearContents
Range("A1:D1") = Array("LastName", "FirstName", "Employee ID", "Duration")
lastRow = Range("A" & Rows.Count).End(xlUp).Row
Range("A1:C" & lastRow).RemoveDuplicates Columns:=Array(1, 2, 3)
lastRow = Range("A" & Rows.Count).End(xlUp).Row
For x = 2 To lastRow
Cells(x, "D").Value = dicIDs(Cells(x, "C").Value)
Next
Columns.AutoFit
Application.ScreenUpdating = True
End Sub
If you are not familiar with using a Scripting Dictionary then this is a must see Excel VBA Introduction Part 39 - Dictionaries

Dynamic range like with indirect in VBA

I haven't used VBA before but I have found an example/working workbook that calculates just what i need. I've put this into the workbook I am working on but the problem is I need to set the data range by row number and leave the column static and have no idea how to do it although I have tried. This is the code I found that is working fine but only with a static range.
Sub UpdatePairStats()
Dim LRange As Variant
Dim LRows As Long
Dim LCols As Long
Dim C As New Collection
Dim LItem As Long
Dim LDesc As String
Dim Counts(10000, 4) As String
Dim i As Long, j As Long, k As Long
On Error Resume Next
'Select sheet where data resides
Sheets("Draw Data").Select
'Data range (where draw information resides)
LRange = Range("C2:H1151")
LRows = UBound(LRange, 1)
LCols = UBound(LRange, 2)
'Loop through each row in LRange (find pairs)
For i = 1 To LRows
'j and k create the pairs
For j = 1 To LCols - 1
For k = j + 1 To LCols
'Separate pairs with a "." character (smaller number first)
If LRange(i, j) < LRange(i, k) Then
LDesc = LRange(i, j) & "." & LRange(i, k)
Else
LDesc = LRange(i, k) & "." & LRange(i, j)
End If
'Add new item to collection ("on error resume next" is
'required above in this procedure because of this line of code)
C.Add C.Count + 1, LDesc
'Retrieve indexnumber of new item
LItem = C(LDesc)
'Add pair information to new item
If Counts(LItem, 0) = "" Then
Counts(LItem, 0) = LDesc
Counts(LItem, 1) = LRange(i, j)
Counts(LItem, 2) = LRange(i, k)
End If
'Increment stats counter
If Counts(LItem, 3) = "" Then
Counts(LItem, 3) = "1"
Else
Counts(LItem, 3) = CStr(CInt(Counts(LItem, 3)) + 1)
End If
Next k
Next j
Next i
'Paste pairs onto sheet called PairStats
Sheets("PairStats").Select
Cells.Select
Selection.Clear
Cells(1, 1).Resize(C.Count, 4) = Counts
'Format headings
Range("A1").FormulaR1C1 = "'Number1.Number2"
Range("B1").FormulaR1C1 = "'Number1"
Range("C1").FormulaR1C1 = "'Number2"Range("D1").FormulaR1C1 = "'Occurrences"
Range("A1:D1").Select
Selection.Font.Bold = True
Selection.Font.Underline = xlUnderlineStyleSingle
Columns("A:D").EntireColumn.AutoFit
Range("F1").Select
Range("F1").FormulaR1C1 = "Last Updated on " & Now()
Sheets("Pairs").Select
MsgBox "Pair statistics have been updated."
End Sub
The range I need to set is
'Data range (where draw information resides)
LRange = Range("C2:H1151")
I have other calculations working fine (not in VBA) by using INDIRECT to get the row value from two separate cells but would like to know how to implement the same sort of thing in VBA. The formula I'm using is
=IFERROR(FREQUENCY(INDIRECT("'Draw Data'!$C"&B2&":$H"&B3),$N$3:$N$13),0)
I've read INDIRECT can't be used in VBA but is there a simple bit of code that can do the same job?
First you need to know the last row with data and you can do that by:
Dim LRwithdata As Long
With Sheets("Draw Data")
LRwithdata = .Range("C:H").Find("*", , , , , xlPrevious).Row
LRange = .Range("C2:H" & LRwithdata)
End With
' rest of your code here
Edit1: If rows are referenced to other cells
With Sheets("Draw Data")
LRange = .Range("C" & .Range("B2"), "H" & .Range("B3"))
End With
The key is to be familiar with Range Syntax and apply it accordingly when referring to a range.
You can check below links as well so you can improve your coding:
Avoid using Select/Activate
Finding the last row
Range Syntax