Efficient way to match/merge several ranges or arrays by date - excel

i am not be able to make this computation efficiently with excel (vba):
Input
Output
Doing this with tables is incredible slow, when you have a few rows and columns is working perfectly, but is no practical when you increase the number of series and rows.
What i do is update Output Table with VBA, steps:
Delete data of Output Listobject Table
Resize Listobject Range with number of dates between (min max Dates1, Dates,2)
Generate Dates and dump it in the Output Listobject Table Dates column.
I get the matching with this formula array formula in each Result row in the output listobject table:
=SUM(IF((DAY(T_1[Date])=DAY([#Date]))*(MONTH(T_1[Date])=MONTH([#Date]))*(YEAR(T_1[Date])=AÑO([#Date]));T_1[Result1]))
The Number of Series is dinamic and rows will be dynamic, i have up to 30 columns and 5000 rows. Could you give me some example or approach to achieve this more efficiently?
Here is a table with time execution speed of participants snippets. Tested with the whole data. 3161 rows x 40 columns (20 Results columns to match):
Execution time table

The following does not list the dates in order, but collects data for each input date. It is similar to the pivot table.
Compare the execution speed with other code.
Sub MergeData()
Dim strU As String
Dim myWs As Worksheet, Ws As Worksheet
Dim vTable() As Variant
Dim vFid1(), vFid2()
Dim k As Integer, n As Integer, c As Integer
Dim sWsName As String, s As String
Dim strSQL As String
Set myWs = Sheets(1) '<~~ Your data Sheet
Set Ws = Sheets(2) '<~~ Result Sheet
sWsName = myWs.Name & "$"
With myWs
c = .Cells(1, Columns.Count).End(xlToLeft).Column
For i = 1 To c Step 2
n = n + 1
ReDim Preserve vTable(1 To n)
ReDim Preserve vFid1(1 To n)
ReDim Preserve vFid2(1 To n)
vTable(n) = sWsName & .Cells(1, i).Resize(65536, 2).Address(0, 0)
vFid1(n) = "[" & .Cells(1, i) & "]"
vFid2(n) = "[" & .Cells(1, i + 1) & "]"
Next i
End With
For k = 1 To n - 1
s = Replace(vFid2(k), "[", "")
s = Replace(s, "]", "")
strU = strU & "SELECT " & vFid1(k) & " as Dates ," & vFid2(k) & " as Result , '" & s & "' as myPivot " & " FROM [" & vTable(k) & "] where not isnull(" & vFid1(k) & ") union All "
Next k
s = Replace(vFid2(n), "[", "")
s = Replace(s, "]", "")
strU = strU & "SELECT " & vFid1(n) & " as Dates," & vFid2(n) & " as Result, '" & s & "' as myPivot " & " FROM [" & vTable(n) & "] where not isnull(" & vFid1(n) & ") "
strSQL = "TRANSFORM MAX(Result) "
strSQL = strSQL & "SELECT Dates FROM "
strSQL = strSQL & "(" & strU & ") "
strSQL = strSQL & "GROUP BY Dates "
strSQL = strSQL & "ORDER BY Dates "
strSQL = strSQL & "PIVOT myPivot "
exeSQL Ws, strSQL
Ws.Range("a1").CurrentRegion.SpecialCells(xlCellTypeBlanks).Value = 0
End Sub
Sub exeSQL(Ws As Worksheet, strSQL As String)
Dim Rs As Object
Dim strConn As String
Dim i As Integer
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.FullName & ";" & _
"Extended Properties=Excel 12.0;"
Set Rs = CreateObject("ADODB.Recordset")
Rs.Open strSQL, strConn
If Not Rs.EOF Then
With Ws
.Range("a1").CurrentRegion.ClearContents
For i = 0 To Rs.Fields.Count - 1
.Cells(1, i + 1).Value = Rs.Fields(i).Name
Next
.Range("a" & 2).CopyFromRecordset Rs
End With
End If
Rs.Close
Set Rs = Nothing
End Sub
Data image
you can extend over 30 series. This is only 5 series.
Result image

First, I have made the assumption that you have dates and results in adjacent columns in the form of Dates N | Results N, image below.
Second, I have written the below code which should solve your problem. Note: this is not completely scalable as is, but you can use this now to progress further and modify to your needs. Also, please excuse my poor maths to calculate the out_col_num variable.
Option Explicit
Sub Merge_Dates()
'variables to set up dates
Dim lYear As Long: lYear = 2020
Dim lMonth As Long: lMonth = 3
Dim lDay As Long
'arrays
Dim arr_in() As Variant
Dim arr_out() As Variant
Dim x_in As Long, y_in As Long
Dim x_out As Long, y_out As Long
Dim out_col_num As Long, n As Long: n = 1
arr_in = ActiveSheet.UsedRange.Value
'we need to define the bounds for the output array
'this will contain all dates for March (in this example)
'also hold the results in the columns - this is a function on lbound(2)
ReDim arr_out(1 To 32, 1 To (UBound(arr_in, 2) - 1))
'header for out array
arr_out(1, 1) = "Dates"
'load dates
For lDay = 1 To 31
arr_out(lDay + 1, 1) = CDate(Format(DateSerial(lYear, lMonth, lDay), "DD/MM/YYYY"))
Next lDay
'set column headers
For x_out = LBound(arr_out, 2) + 1 To UBound(arr_out, 2)
arr_out(1, x_out) = "Results" & (x_out - 1)
Next x_out
'now loop through in array and map to out array
'you can do this multiple ways, below is just one
'loop x dim in array
For x_in = LBound(arr_in, 2) To UBound(arr_in, 2) Step 2
'loop y dim in array
For y_in = LBound(arr_in, 1) + 1 To UBound(arr_in, 1)
'loop y dim out array to store result
For y_out = LBound(arr_out, 1) + 1 To UBound(arr_out, 1)
If arr_out(y_out, 1) = arr_in(y_in, x_in) Then
'out column is a function of in column
'-n + 3n
out_col_num = (-1 * x_in) + (3 * n)
arr_out(y_out, out_col_num) = arr_in(y_in, x_in + 1)
Exit For
End If
Next y_out
Next y_in
'increment n
n = n + 1
Next x_in
'output
ActiveSheet.Range("A10").Resize(UBound(arr_out, 1), UBound(arr_out, 2)).Value = arr_out
End Sub
So given the example, assuming your dates cover only March 2020 (something you will have to modify to build it more scalable):
Will give the output as below:

I've put something together using ADODB Recordsets, so that I can use .Filter and .Find. This code outputs the unique dates, and then the result on that date for each result set.
Const AD_DATE = 7
Const AD_VARIANT = 12
Const AD_BIGINT = 20
Const AD_VARCHAR = 200
Const AD_FILTERNONE = 0
Sub sResultData()
On Error GoTo E_Handle
Dim aResultSet() As String
Dim lngMaxCol As Long
Dim lngMaxRow As Long
Dim lngLoopRow As Long
Dim lngLoopCol As Long
Dim rsMaster As Object
Dim rsDate As Object
Set rsMaster = CreateObject("ADODB.Recordset")
Set rsDate = CreateObject("ADODB.Recordset")
lngMaxRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
lngMaxCol = ActiveSheet.Cells(1, ActiveSheet.Columns.Count).End(xlToLeft).Column
With rsMaster.Fields
.Append "ResultDate", AD_DATE
.Append "ResultSet", AD_VARCHAR, 50
.Append "ResultData", AD_BIGINT
End With
With rsDate.Fields
.Append "ResultDate", AD_DATE
End With
rsMaster.Open
rsDate.Open
ReDim aResultSet(1 To lngMaxCol / 2)
For lngLoopCol = 2 To lngMaxCol Step 2
aResultSet(lngLoopCol / 2) = ActiveSheet.Cells(1, lngLoopCol)
Next lngLoopCol
For lngLoopRow = 2 To lngMaxRow
For lngLoopCol = 2 To lngMaxCol Step 2
With rsMaster
.AddNew
!ResultDate = ActiveSheet.Cells(lngLoopRow, lngLoopCol - 1)
!ResultSet = ActiveSheet.Cells(1, lngLoopCol)
!ResultData = ActiveSheet.Cells(lngLoopRow, lngLoopCol)
.Update
End With
If (rsDate.BOF And rsDate.EOF) Then ' dealing with first record, so cannot do .Find
rsDate.AddNew
rsDate!ResultDate = ActiveSheet.Cells(lngLoopRow, lngLoopCol - 1)
rsDate.Update
Else
rsDate.MoveFirst
rsDate.Find "ResultDate=" & Format(ActiveSheet.Cells(lngLoopRow, lngLoopCol - 1), "dd/mmm/yyyy")
If (rsDate.EOF) Or (rsDate.EOF) Then
rsDate.AddNew
rsDate!ResultDate = ActiveSheet.Cells(lngLoopRow, lngLoopCol - 1)
rsDate.Update
End If
End If
Next lngLoopCol
Next lngLoopRow
rsDate.Sort = "ResultDate ASC"
rsDate.MoveFirst
rsMaster.Sort = "ResultSet ASC, ResultDate ASC"
For lngLoopCol = 1 To UBound(aResultSet)
lngLoopRow = lngMaxRow + 5
ActiveSheet.Cells(lngLoopRow - 1, lngLoopCol + 1) = aResultSet(lngLoopCol)
rsMaster.Filter = AD_FILTERNONE
rsMaster.Filter = "ResultSet='" & aResultSet(lngLoopCol) & "'"
rsDate.MoveFirst
Do
ActiveSheet.Cells(lngLoopRow, 1) = rsDate!ResultDate
rsMaster.MoveFirst
rsMaster.Find "ResultDate=#" & Format(rsDate!ResultDate, "dd-mmm-yy") & "#"
If Not rsMaster.EOF Then
ActiveSheet.Cells(lngLoopRow, lngLoopCol + 1) = rsMaster!ResultData
End If
lngLoopRow = lngLoopRow + 1
rsDate.MoveNext
Loop Until rsDate.EOF
Next lngLoopCol
sExit:
On Error Resume Next
rsDate.Close
rsMaster.Close
Set rsDate = Nothing
Set rsMaster = Nothing
Exit Sub
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "sResultData", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume sExit
End Sub
Regards,

Test the next code, please. It will deal with as many (pairs of) columns you will have. It determines the minimum, respectively, maximum used date and iterates between the determined interval, collecting data in arrFin array. You can also use any Date interval. The interval will be automatically determined. My code drops the values one column after the existing range. This is done only for testing reasons. I have to test it in a way... You can drop them wherever you need. So, if you intend to run the code for the second time, you must delete the previously returned values.
Sub testMatchReArrange()
Dim sh As Worksheet, arrD As Variant, DateRng As Range, lastCol As Long, lastRow As Long
Dim i As Long, dateStart As Date, dateFinish As Date, dDiff As Long, arrFin As Variant
Dim boolFound As Boolean, checkDate As Date, j As Long, k As Long, f As Long
Set sh = ActiveSheet 'use here your sheet
lastRow = sh.Range("A" & Rows.count).End(xlUp).Row
lastCol = sh.Cells(1, Columns.count).End(xlToLeft).Column
arrD = sh.Range(sh.Cells(2, 1), Cells(lastRow, lastCol)).value 'array to be processed
'create the specific range keeping only Date, in order to determine the correct date interval. Especially the minimum date...
For i = 1 To lastCol Step 2
If DateRng Is Nothing Then
Set DateRng = sh.Range(sh.Cells(2, i), sh.Cells(lastRow, i))
Else
Set DateRng = Union(DateRng, sh.Range(sh.Cells(2, i), sh.Cells(lastRow, i)))
End If
Next i
dateStart = WorksheetFunction.Min(DateRng) 'starting date
dateFinish = WorksheetFunction.Max(DateRng) 'finishing date
dDiff = dateFinish - dateStart 'the date interval to be processed
'Properly dimension the array to collect the processing result:
ReDim arrFin(1 To dDiff + 2, 1 To lastCol / 2 + 1): f = 1
'Load the head of columns:
arrFin(1, 1) = "Dates"
For i = 2 To lastCol / 2 + 1
arrFin(1, i) = "result" & i - 1
Next i
f = 2 're-initializing the row of for real processed data
checkDate = dateStart 'initialize the date to be used for processing
For i = 1 To dDiff + 1 'for each date in the processed date interval
For j = 1 To UBound(arrD, 1) 'for each row in the processed array
For k = 1 To UBound(arrD, 2) Step 2 'for each column in the processed array (but looking only in add columns)
If CDate(arrD(j, k)) = checkDate Then
arrFin(f, 1) = checkDate: arrFin(f, (k + 1) / 2 + 1) = arrD(j, k + 1)
boolFound = True 'confirming that at least a match exist
End If
Next k
Next j
If Not boolFound Then arrFin(f, 1) = checkDate' Record the date in case of no any match
boolFound = False: f = f + 1
checkDate = checkDate + 1
Next i
'you can use here any other location (sheet, range) to drop the resulted array:
sh.Cells(1, lastCol + 2).Resize(UBound(arrFin, 1), UBound(arrFin, 2)).value = arrFin
End Sub
In case of a big range, it needs some time, but working only in memory (using arrays) it is the maximum possible speed for such a task.

The code below allows you to specify the columns from which to collect the data and outputs the result on a dedicated sheet which would need to be inserted for that purpose. I called it "Output" but you can pick a name that suits you better. Your original data will not be touched.
Sub MergeDataByDate()
' 006
' define the origin of your data
Const FirstDataRow As Long = 2 ' applicable to both data sets
' set the columns to what they are on your sheet (A = 1, B = 2 etc)
Const C1 As Long = 2 ' Date 1 column
Const Cr1 As Long = 3 ' Result 1 column
Const C2 As Long = 8 ' Date 2 column
Const Cr2 As Long = 11 ' Result 2 column
Dim WsOut As Worksheet ' worksheet for output
Dim ArrIn As Variant ' for input
Dim Arr() As Variant ' for output
Dim Dat As Date ' date counter
Dim Rng As Range
Dim i As Long ' Arr index
Dim R As Long ' row counter
Set WsOut = Worksheets("Output") ' the output sheet must exist: rename to suit
With Worksheets("Input") ' use your tab's name
Set Rng = .Range(.Cells(FirstDataRow, 1), _
.Cells(.Rows.Count, C1).End(xlUp) _
.Offset(0, Cr2 - C1))
ArrIn = Rng.Value
ReDim Arr(1 To 3, (2 * UBound(ArrIn)))
For R = 1 To UBound(ArrIn)
Arr(1, i) = ArrIn(R, C1)
Arr(2, i) = ArrIn(R, Cr1)
Arr(1, i + 1) = ArrIn(R, C2)
Arr(3, i + 1) = ArrIn(R, Cr2)
i = i + 2
Next R
End With
Application.ScreenUpdating = False
With WsOut
Set Rng = .Cells(2, 1).Resize(UBound(Arr, 2), UBound(Arr))
Rng.Value = Application.Transpose(Arr)
With .Sort
With .SortFields
.Clear
.Add Key:=Rng.Cells(1), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
End With
.SetRange Rng
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End With
Arr = Rng.Value
For R = (UBound(Arr) - 1) To 1 Step -1
If Arr(R + 1, 1) = Arr(R, 1) Then
Arr(R, 2) = Arr(R, 2) + Arr(R + 1, 2)
Arr(R, 3) = Arr(R, 3) + Arr(R + 1, 3)
For i = 1 To 3
Arr(R + 1, i) = vbNullString
Next i
Else
Arr(R, 2) = Val(Arr(R, 2)) + 0
Arr(R, 3) = Val(Arr(R, 3)) + 0
End If
Next R
Rng.Value = Arr
With WsOut ' sort blanks to the bottom
With .Sort
With .SortFields
.Clear
.Add Key:=Rng.Cells(1), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
End With
.SetRange Rng
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
R = .Cells(.Rows.Count, 1).End(xlUp).Row
Dat = CLng(Cells(R, 1).Value)
For R = R To 3 Step -1
Dat = Dat - 1
Do Until .Cells(R - 1, 1).Value = Dat
.Rows(R).Insert
.Cells(R, 1).Value = Dat
.Cells(R, 2).Value = 0
.Cells(R, 3).Value = 0
Dat = Dat - 1
Loop
Next R
End With
Application.ScreenUpdating = True
End Sub
The code first combines the existing data to a single list, then sorts the list by date. It then unites data from the same days into single lines, deleting the lines that become redundant and sort them to the end of the list where they disappear.
In the last step the remaining data are checked for dates and missing dates are inserted between the last first date in the list and the last. If you are particular about those dates, for example, you want them to be the first and last days of a month it's the most efficient to add those two days with zero results anywhere in the original data. If results for those dates exist the zero values will be discarded. If they don't they, and any intervening days, will be added to the output.

Took me a while, but here is my code:
Sub SubOutput()
'Declarations.
Dim WksInput As Worksheet
Dim WksOutput As Worksheet
Dim RngInputFirstCell As Range
Dim RngOutputFirstCell As Range
Dim BytOffset As Byte
Dim RngRange01 As Range
Dim RngTarget As Range
Dim BytWholeCalendar As Byte
Dim DatFirstDate As Date
Dim DatLastDate As Date
Dim IntCounter01 As Integer
'Setting variables.
Set WksInput = Sheets("Input") 'put here the name of the worksheet with input data
Set WksOutput = Sheets("Output") 'put here the name of the worksheet with the output data
Set RngInputFirstCell = WksInput.Range("A1") 'put here the top left cell of the input data (the one with value Dates1)
Set RngOutputFirstCell = WksOutput.Range("A1") 'put here the top left cell of the output data (the one with value Dates)
'Asking what days are to be reported.
BytWholeCalendar = MsgBox("Do you need the output to report data for every day?", vbYesNoCancel, "Report every day?")
'In case of no answer, the subroutine is terminated.
If BytWholeCalendar <> 6 And BytWholeCalendar <> 7 Then
Exit Sub
End If
'Typing "Dates" in RngOutputFirstCell.
RngOutputFirstCell = "Dates"
'Covering the entire input.
Do Until RngInputFirstCell.Offset(0, BytOffset * 2) = ""
'Setting first part of the range to be copied (dates).
Set RngRange01 = WksInput.Range(RngInputFirstCell.Offset(1, BytOffset * 2), WksInput.Cells(WksInput.Rows.Count, RngInputFirstCell.column + BytOffset * 2).End(xlUp))
'Setting the range where to paste the dates.
Set RngTarget = WksOutput.Cells(WksOutput.Rows.Count, RngOutputFirstCell.column).End(xlUp).Offset(1, 0)
Set RngTarget = RngTarget.Resize(RngRange01.Rows.Count)
'Pasting the dates.
RngTarget.Value = RngRange01.Value
'Copying the result name.
RngOutputFirstCell.Offset(0, BytOffset + 1).Value = RngInputFirstCell.Offset(0, BytOffset * 2 + 1).Value
'Setting BytOffset to cover the next rows of data.
BytOffset = BytOffset + 1
Loop
'Editing the dates according to BytWholeCalendar.
Select Case BytWholeCalendar
Case Is = 6
'Setting variables.
DatFirstDate = Excel.WorksheetFunction.Min(WksOutput.Range(RngOutputFirstCell.Offset(1, 0), RngOutputFirstCell.End(xlDown)))
DatLastDate = Excel.WorksheetFunction.Max(WksOutput.Range(RngOutputFirstCell.Offset(1, 0), RngOutputFirstCell.End(xlDown)))
IntCounter01 = 1
'Clearing dates.
WksOutput.Range(RngOutputFirstCell.Offset(1, 0), RngOutputFirstCell.End(xlDown)).ClearContents
'Filling dates.
For DatFirstDate = DatFirstDate To DatLastDate
RngOutputFirstCell.Offset(IntCounter01, 0).Value = DatFirstDate
IntCounter01 = IntCounter01 + 1
Next DatFirstDate
Case Is = 7
'Sorting output dates.
With WksOutput.Sort
.SortFields.Clear
.SortFields.Add Key:=RngOutputFirstCell, _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
.SetRange Range(RngOutputFirstCell, RngOutputFirstCell.End(xlDown))
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'Marking unique dates.
Set RngTarget = WksOutput.Range(RngOutputFirstCell.Offset(1, 1), RngOutputFirstCell.End(xlDown).Offset(0, 1))
RngTarget.FormulaR1C1 = "=IF(RC[-1]=R[1]C[-1],"""",""X"")"
RngTarget.Value = RngTarget.Value
'Sorting output dates by unique values.
With WksOutput.Sort
.SortFields.Clear
.SortFields.Add Key:=RngOutputFirstCell.Offset(0, 1), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
.SetRange Range(RngOutputFirstCell.Offset, RngOutputFirstCell.End(xlDown).Offset(0, 1))
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'Clearing double dates.
With WksOutput.Range(RngOutputFirstCell.End(xlDown), RngOutputFirstCell.Offset(0, 1).End(xlDown).Offset(1, 0))
.ClearContents
.ClearFormats
End With
End Select
'Setting RngTarget to cover the results' part of the output.
Set RngTarget = WksOutput.Range(RngOutputFirstCell.Offset(1, 1), RngOutputFirstCell.End(xlDown).Offset(0, 1))
Set RngTarget = RngTarget.Resize(, BytOffset)
RngTarget.FormulaR1C1 = "=VLOOKUP(RC" & RngOutputFirstCell.column & ",OFFSET(INDIRECT(""" & WksInput.Name & "!R" & RngInputFirstCell.Row + 1 & "C""" & " & MATCH(R" & RngOutputFirstCell.Row & "C," & WksInput.Name & "!" & WksInput.Range(RngInputFirstCell, RngInputFirstCell.End(xlToRight)).Address(, , xlR1C1) & ",0) + " & RngInputFirstCell.column - 1 & ",FALSE),0,-1,5000,2),2,FALSE)"
'Typing in RngTarget the formula.
'RngTarget.FormulaR1C1 = "=IFERROR(VLOOKUP(RC" & RngOutputFirstCell.column & ",OFFSET(INDIRECT(""" & WksInput.Name & "!R" & RngInputFirstCell.Row + 1 & "C""" & " & MATCH(R" & RngOutputFirstCell.Row & "C," & WksInput.Name & "!" & WksInput.Range(RngInputFirstCell, RngInputFirstCell.End(xlToRight)).Address(, , xlR1C1) & ",0) + " & RngInputFirstCell.column - 1 & ",FALSE),0,-1,5000,2),2,FALSE),0)"
'Transforming formulas into values.
'RngTarget.Value = RngTarget.Value
'Setting RngTarget to select the output data.
Set RngTarget = RngTarget.Offset(0, -1).Resize(, RngTarget.Columns.Count + 1)
'Formatting.
With RngTarget
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.Borders(xlDiagonalDown).LineStyle = xlNone
.Borders(xlDiagonalUp).LineStyle = xlNone
.Borders(xlInsideHorizontal).LineStyle = xlNone
With .Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeRight)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlInsideVertical)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
End With
'Setting RngTarget to select the output labels.
Set RngTarget = RngTarget.Offset(-1, 0).Resize(1)
'Formatting.
With RngTarget
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.Borders(xlDiagonalDown).LineStyle = xlNone
.Borders(xlDiagonalUp).LineStyle = xlNone
.Borders(xlInsideHorizontal).LineStyle = xlNone
With .Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeRight)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlInsideVertical)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
End With
RngTarget.EntireColumn.AutoFit
Debug.Print "REPORT"; " | "
Debug.Print "WksInput.Parent.Name = WksOutput.Parent.Name ? "; WksInput.Parent.Name = WksInput.Parent.Name; " | "
Debug.Print "WksInput.Name ? "; WksInput.Name; " | "
Debug.Print "RngInputFirstCell.Address ? "; RngInputFirstCell.Address; " | "
Debug.Print "RngInputFirstCell.Value ? "; RngInputFirstCell.Value; " | "
Debug.Print "RngInputFirstCell.Formula ? "; RngInputFirstCell.Formula; " | "
Debug.Print "RngInputFirstCell.Offset(1,0).Address ? "; RngInputFirstCell.Offset(1, 0).Address; " | "
Debug.Print "RngInputFirstCell.Offset(1,0).Value ? "; RngInputFirstCell.Offset(1, 0).Value; " | "
Debug.Print "RngInputFirstCell.Offset(1,0).Formula ? "; RngInputFirstCell.Offset(1, 0).Formula; " | "
Debug.Print "RngInputFirstCell.Offset(0,1).Address ? "; RngInputFirstCell.Offset(0, 1).Address; " | "
Debug.Print "RngInputFirstCell.Offset(0,1).Value ? "; RngInputFirstCell.Offset(0, 1).Value; " | "
Debug.Print "RngInputFirstCell.Offset(0,1).Formula ? "; RngInputFirstCell.Offset(0, 1).Formula; " | "
Debug.Print "RngInputFirstCell.Offset(1,1).Address ? "; RngInputFirstCell.Offset(1, 1).Address; " | "
Debug.Print "RngInputFirstCell.Offset(1,1).Value ? "; RngInputFirstCell.Offset(1, 1).Value; " | "
Debug.Print "RngInputFirstCell.Offset(1,1).Formula ? "; RngInputFirstCell.Offset(1, 1).Formula; " | "
Debug.Print "RngInputFirstCell.Offset(91,0).Address ? "; RngInputFirstCell.Offset(91, 0).Address; " | "
Debug.Print "RngInputFirstCell.Offset(91,0).Value ? "; RngInputFirstCell.Offset(91, 0).Value; " | "
Debug.Print "RngInputFirstCell.Offset(91,0).Formula ? "; RngInputFirstCell.Offset(91, 0).Formula; " | "
Debug.Print "RngInputFirstCell.Offset(91,1).Address ? "; RngInputFirstCell.Offset(91, 1).Address; " | "
Debug.Print "RngInputFirstCell.Offset(91,1).Value ? "; RngInputFirstCell.Offset(91, 1).Value; " | "
Debug.Print "RngInputFirstCell.Offset(91,1).Formula ? "; RngInputFirstCell.Offset(91, 1).Formula; " | "
Debug.Print "WksOutput.Name ? "; WksOutput.Name; " | "
Debug.Print "RngOutputFirstCell.Address ? "; RngOutputFirstCell.Address; " | "
Debug.Print "RngOutputFirstCell.Value ? "; RngOutputFirstCell.Value; " | "
Debug.Print "RngOutputFirstCell.Formula ? "; RngOutputFirstCell.Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(1,0).Address ? "; RngOutputFirstCell.Offset(1, 0).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(1,0).Value ? "; RngOutputFirstCell.Offset(1, 0).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(1,0).Formula ? "; RngOutputFirstCell.Offset(1, 0).Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(0,1).Address ? "; RngOutputFirstCell.Offset(0, 1).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(0,1).Value ? "; RngOutputFirstCell.Offset(0, 1).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(0,1).Formula ? "; RngOutputFirstCell.Offset(0, 1).Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(1,1).Address ? "; RngOutputFirstCell.Offset(1, 1).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(1,1).Value ? "; RngOutputFirstCell.Offset(1, 1).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(1,1).Formula ? "; RngOutputFirstCell.Offset(1, 1).Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(91,0).Address ? "; RngOutputFirstCell.Offset(91, 0).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(91,0).Value ? "; RngOutputFirstCell.Offset(91, 0).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(91,0).Formula ? "; RngOutputFirstCell.Offset(91, 0).Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(91,1).Address ? "; RngOutputFirstCell.Offset(91, 1).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(91,1).Value ? "; RngOutputFirstCell.Offset(91, 1).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(91,1).Formula ? "; RngOutputFirstCell.Offset(91, 1).Formula; " | "
End Sub
Bit long, yep. Still it should work. Just make sure to properly set those 4 variables at the beginning (WksInput, WksOutput, RngInputFirstCell, RngOutputFirstCell). Notes will guide you. The code writes on previous output but it doesn't clear it (still it can be modify accordingly). It also apply part of the format you've used in your examples (with more details it's possible to completely edit the format).
If you need any clarification, just say please.

Related

speed up Excel vba program

Kindly see below code where it takes too much time run for more than 30rows in a range. (its similar to knapsack algorithm requirements)
let me try to explain below in detail,
Input Base sheet: Column A having values (For ex: 1555),
Column B having its Assignment value (A1),
Column C & D its filter value which will perform against input data sheet file.
Program working concept:
it takes first row(2) data from base sheet and apply filter (C2 & D2 value) in input data sheet (Column A & B respectively) then it checks value in column C and it find best sum to match the value (1555) or nearest to it and after it assigns value (which is A1) against those rows and repeats the same for next rows.
I have posted image below.
Kindly refer for Input Base sheet and Input Data sheet and
copy the codes in another workbook.
Run the macro, Choose Base sheet and the Data sheet. Program would run and assigns in Input data sheet. It runs super fast in lesser rows when I have more rows it gets hang/takes too hours to run.
Help me to where it can be speed up.
Appreciate your supports.
Thanks
input base sheet
input data sheet:
Sub sample1()
Dim lrow As Integer
Dim frow As Integer
Dim row As Integer
Dim ar As Variant
Dim aar As Variant
Dim Sol(), csol()
Dim arr As Variant
Dim pos As Integer
Dim arow() As Integer
Dim rng As Range
Dim rn As Range
Dim r As Range
Dim k As Integer
Dim itr As Integer
Dim path As String
Dim tm_base As Workbook
Dim tm_data As Workbook
Dim sh_base As Worksheet
Dim sh_data As Worksheet
Dim sh As Worksheet
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Set sh = ActiveSheet
ReDim arr(0)
arr(0) = ""
path = FileSelection("Input Base")
If path = "" Then Exit Sub
Set tm_base = Workbooks.Open(path)
path = FileSelection("Input Data")
If path = "" Then Exit Sub
Set tm_data = Workbooks.Open(path)
Set sh_base = tm_base.ActiveSheet
Set sh_data = tm_data.ActiveSheet
lrow = sh_data.Cells(Rows.Count, "A").End(xlUp).row
frow = sh_base.Cells(Rows.Count, "A").End(xlUp).row
SortMacro ActiveSheet, sh_base.Range("A2:A" & frow), sh_base.Range("A1:G" & frow), 2
SortMacro ActiveSheet, sh_data.Range("C2:C" & lrow), sh_data.Range("A1:G" & lrow), 2
For row = 2 To frow
If sh_base.Cells(row, "H") <> "Done" Then
itr = 1
sh_data.Range("A1:G" & lrow).AutoFilter Field:=4, Criteria1:="="
op2:
sh_data.Range("A1:G" & lrow).AutoFilter Field:=1, Criteria1:=sh_base.Cells(row, "C").Value
sh_data.Range("A1:G" & lrow).AutoFilter Field:=2, Criteria1:=sh_base.Cells(row, "D").Value
Set rn = Nothing
On Error Resume Next
Set rn = sh_data.Range("C2:C" & lrow).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rn Is Nothing Then
ReDim ar(0)
ReDim arow(0)
k = 1
For Each r In rn
ReDim Preserve arow(k)
ReDim Preserve ar(k)
ar(k) = r.Value
arow(k) = r.row
k = k + 1
Next
ReDim Sol(LBound(ar) To UBound(ar))
ReDim csol(LBound(ar) To UBound(ar))
limsum = sh_base.Cells(row, "A").Value
For i = LBound(ar) To UBound(ar)
If ar(i) > limsum Then
ar(i) = -1
End If
Next
maxsum = 0
findsum ar, Sol, csol, maxsum, limsum, UBound(ar), UBound(ar)
ss = ""
For i = 1 To Sol(0)
'ss = ss & sep & ar(sol(i))
'sep = ","
If Not arr(UBound(arr)) = "" Then
ReDim Preserve arr(UBound(arr) + 1)
End If
arr(UBound(arr)) = ar(Sol(i))
Next i
'MsgBox ss & " sum =" & maxsum
For j = LBound(arr) To UBound(arr)
pos = Application.Match(arr(j), ar, False)
If ar(pos - 1) > 0 Then
ar(pos - 1) = -1
End If
pos = arow(pos - 1)
If sh.Range("B1") = "Option 01" Then
sh_data.Cells(pos, "D") = sh_base.Cells(row, "B").Value
Else
sh_data.Cells(pos, "D") = sh_base.Cells(row, "B").Value & " " & Format(itr, "00")
End If
Next
ReDim arr(0)
arr(0) = ""
sh_base.Cells(row, "H") = "Done"
If sh.Range("B1") = "Option 02" Then
sh_data.Range("A1:G" & lrow).AutoFilter Field:=4, Criteria1:="="
Set rng = sh_data.Range("A1:A" & lrow).SpecialCells(xlCellTypeVisible)
If rng.Cells.Count > 1 Then
itr = itr + 1
GoTo op2
End If
End If
End If
sh_data.Range("A1:G" & frow).AutoFilter Field:=1
sh_data.Range("A1:G" & frow).AutoFilter Field:=2
sh_data.Range("A1:G" & lrow).AutoFilter Field:=4
End If
Next
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Sub findsum(ByVal a, ByRef Sol, ByRef csol, ByRef maxsum, ByRef limsum, si, maxcount, Optional s = 0, Optional lvl = 1, Optional dif = 100000, Optional minuscount = 0, Optional tsol, Optional j = 0)
' recursive sub
For i = LBound(a) To si
If a(i) > 0 Then
If s + a(i) > limsum Then findsum a, Sol, csol, maxsum, limsum, i - 1, maxcount, s, lvl + 1, dif, minuscount, tsol
s = s + a(i)
csol(lvl) = i
If s <= limsum Then
If s > maxsum Then ' we found a sum greater than current max we save it
maxsum = s
Sol(0) = lvl
For j = 1 To lvl
Sol(j) = csol(j)
Next j
End If
If i > LBound(a) Then ' pick another number
findsum a, Sol, csol, maxsum, limsum, i - 1, maxcount, s, lvl + 1, dif, minuscount, tsol
End If
End If
s = s - a(i)
If maxsum = limsum Then Exit For 'exit if exact match
End If
Next i
End Sub
Sub SortMacro(ws As Worksheet, rn As Range, rng As Range, ord As Integer)
ws.Sort.SortFields.Clear
ws.Sort.SortFields.Add Key:=rn, _
SortOn:=xlSortOnValues, Order:=ord, DataOption:=xlSortNormal
With ws.Sort
.SetRange rng
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Function FileSelection(file As String)
Dim path As String
Dim st As String
Dim i As Integer
Dim j As Integer
FileSelection = ""
With Application.FileDialog(3)
.title = "Select the " & file & " file"
.AllowMultiSelect = False
.InitialFileName = st
.Show
If .SelectedItems.Count = 0 Then
MsgBox "You didn't select the file!", vbExclamation, "Canceled"
Exit Function
Else
FileSelection = .SelectedItems(1)
End If
End With
End Function
You can't. I ran it. With 20 base data points and 100 data points you already have sub findsum called 79 million times. It's a combinatorial explosion and no amount of code tweaking will fix that. You'll have to find a better algorithm.

How to show results on desired sheet?

The below code is not showing the results on the "All Stock Analysis" sheet.
I tried doing a test after the activation of each worksheet (Range("I1).Interior.Color = vbGreen) and cell I1 turns green on each of the desired worksheets. What other tests can I try? No error msg pops up.
Sub AllStocksAnalysisRefactored()
Dim startTime As Single
Dim endTime As Single
yearValue = InputBox("What year would you like to run the analysis on?")
startTime = Timer
'Format the output sheet on All Stocks Analysis worksheet
Worksheets("All Stock Analysis").Activate
Range("A1").Value = "All Stocks (" + yearValue + ")"
'Create a header row
Cells(3, 1).Value = "Ticker"
Cells(3, 2).Value = "Total Daily Volume"
Cells(3, 3).Value = "Return"
'Initialize array of all tickers
Dim tickers(12) As String
tickers(0) = "AY"
tickers(1) = "CSIQ"
tickers(2) = "DQ"
tickers(3) = "ENPH"
tickers(4) = "FSLR"
tickers(5) = "HASI"
tickers(6) = "JKS"
tickers(7) = "RUN"
tickers(8) = "SEDG"
tickers(9) = "SPWR"
tickers(10) = "TERP"
tickers(11) = "VSLR"
'Activate data worksheet
Worksheets(yearValue).Activate
'Get the number of rows to loop over
RowCount = Cells(Rows.Count, "A").End(xlUp).Row
'1a) Create a ticker Index
Dim tickerIndex As Single
tickerIndex = 0
'1b) Create three output arrays
Dim tickerVolumes(12) As LongLong
Dim tickerstartingPrices(12) As Single
Dim tickerendingPrices(12) As Single
''2a) Create a for loop to initialize the tickerVolumes to zero.
For i = 0 To 11
tickerVolumes(i) = 0
''2b) Loop over all the rows in the spreadsheet.
For j = 2 To RowCount
'3a) Increase volume for current ticker
tickerVolumes(tickerIndex) = tickerVolumes(tickerIndex) + Cells(j, 8).Value
'3b) Check if the current row is the first row with the selected tickerIndex.
'If Then
If Cells(j - 1, 1).Value <> tickers(tickerIndex) And _
Cells(j, 1).Value = tickers(tickerIndex) Then
tickerstartingPrices(tickerIndex) = Cells(j, 6).Value
'End If
End If
'3c) check if the current row is the last row with the selected ticker
'If the next row’s ticker doesn’t match, increase the tickerIndex.
'If Then
If Cells(j + 1, 1).Value <> tickers(tickerIndex) And _
Cells(j, 1).Value = tickers(tickerIndex) Then
tickerendingPrices(tickerIndex) = Cells(j, 6).Value
'3d Increase the tickerIndex.
tickerIndex = tickerIndex + 1
'End If
End If
Next j
Next i
'4) Loop through your arrays to output the Ticker, Total Daily Volume, and Return.
For i = 0 To 11
Worksheets("All Stock Analysis").Activate
Next i
'Formatting
Worksheets("All Stock Analysis").Activate
Range("A3:C3").Font.FontStyle = "Bold"
Range("A3:C3").Borders(xlEdgeBottom).LineStyle = xlContinuous
Range("B4:B15").NumberFormat = "#,##0"
Range("C4:C15").NumberFormat = "0.0%"
Columns("B").AutoFit
dataRowStart = 4
dataRowEnd = 15
For i = dataRowStart To dataRowEnd
If Cells(i, 3) > 0 Then
Cells(i, 3).Interior.Color = vbGreen
Else
Cells(i, 3).Interior.Color = vbRed
End If
Next i
endTime = Timer
MsgBox "This code ran in " & (endTime - startTime) & _
" seconds for the year " & (yearValue)
End Sub
Here is how "All Stock Analysis" sheet will look after running the code:
You only need to scan the data sheet once if you use a dictionary object to convert the ticker ID to an array index number.
Option Explicit
Sub AllStocksAnalysisRefactored()
Const SHT_NAME = "All Stock Analysis"
Dim wb As Workbook, ws As Worksheet, wsYr As Worksheet
Dim cell As Range, yr As String, iRow As Long, iLastRow As Long
Dim t As Single: t = Timer
' choose data worksheet
yr = InputBox("What year would you like to run the analysis on ? ", "Enter Year", Year(Date))
Set wb = ThisWorkbook
On Error Resume Next
Set wsYr = wb.Sheets(yr)
On Error GoTo 0
' check if exists
If wsYr Is Nothing Then
MsgBox "Sheet '" & yr & "' does not exists.", vbCritical, "Error"
Exit Sub
End If
'Initialize array of all tickers
Dim tickerID, tickerData(), i As Integer, n As Integer
Dim dict As Object, sId As String
tickerID = Array("AY", "CSIQ", "DQ", "ENPH", "FSLR", "HASI", _
"JKS", "RUN", "SEDG", "SPWR", "TERP", "VSLR")
n = UBound(tickerID) + 1
ReDim tickerData(1 To n, 1 To 5)
' create dict id to index
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To n
sId = UCase(Trim(tickerID(i - 1)))
tickerData(i, 1) = sId ' id
tickerData(i, 2) = 0 ' volume
tickerData(i, 3) = 0 ' start price
tickerData(i, 4) = 0 ' finish price
tickerData(i, 5) = 0 ' count
dict.Add sId, i
Next
'Get the number of rows to loop over
iLastRow = wsYr.Cells(Rows.Count, "A").End(xlUp).Row
' Loop over all the rows in the spreadsheet.
' A=ticker, F=Price , H=Volume
For iRow = 2 To iLastRow
sId = UCase(Trim(wsYr.Cells(iRow, "A")))
If dict.exists(sId) Then
i = dict(sId)
' volume
tickerData(i, 2) = tickerData(i, 2) + wsYr.Cells(iRow, "H") ' volume
' start price when count is 0
If tickerData(i, 5) = 0 Then
tickerData(i, 3) = wsYr.Cells(iRow, "F")
End If
' end price
tickerData(i, 4) = wsYr.Cells(iRow, "F")
' count
tickerData(i, 5) = tickerData(i, 5) + 1
End If
Next
'Format the output sheet on All Stocks Analysis worksheet
Set ws = wb.Sheets(SHT_NAME)
ws.Cells.Clear
With ws
.Range("A1").Value2 = "All Stocks (" & yr & ")"
With .Range("A3:E3")
.Value2 = Array("Ticker", "Total Daily Volume", "Start Price", "End Price", "Return")
.Font.FontStyle = "Bold"
.Borders(xlEdgeBottom).LineStyle = xlContinuous
End With
.Range("A4").Resize(n, 4).Value2 = tickerData
.Range("B4:D4").Resize(n).NumberFormat = "#,##0"
.Range("E4").Resize(n).NumberFormat = "0.0%"
.Columns("B").AutoFit
End With
' coloring
For Each cell In ws.Range("E4").Resize(n)
cell.FormulaR1C1 = "=(RC[-1]-RC[-2])/RC[-2]" ' end-start/start
If cell > 0 Then
cell.Interior.Color = vbGreen
Else
cell.Interior.Color = vbRed
End If
Next
ws.Activate
ws.Range("A1").Select
MsgBox "This code ran for (" & yr & ")", vbInformation, Int(Timer - t) & " seconds"
End Sub

Fast way to compare two excel files?

I want to compare 2 excels files [Having only 1 sheet in both] having 10-15 columns and rows will be more than 30K. We got one excel macro file which complete the comparison within 5-10Mins. Limitation of this macro is that it can compare only 2-3 columns at a time. So every time we need to run this macro multiple times which is time consuming process. So I created one utility file [.vbs file] which perform this task in one go but it takes around 1-3Hrs.
Is there any other way to perform this comparison in short time in one go?
startTime=Timer()
Set objExcel=Createobject("Excel.application")
objExcel.Visible=True
Set objWorkbook=objExcel.Workbooks.Open("E:\QTP trial version\Data.xls")
'Set deleteAnalysis_CopySheet=objWorkbook.sheets("Analysis_Copy")
'deleteAnalysis_CopySheet.delete
'Set deleteSummarySheet=objWorkbook.sheets("Summary")
'deleteSummarySheet.delete
Set objAnalysis_Copy=objWorkbook.sheets.add
objAnalysis_Copy.name="Analysis_Copy"
Set objSummary=objWorkbook.sheets.add
objSummary.name="Summary"
objSummary.Cells(1,1)="Analysis Row Count"
objSummary.Cells(2,1)="Reporting Row Count"
objSummary.Cells(3,1)="Analysis Column Count"
objSummary.Cells(4,1)="Reporting Column Count"
objSummary.Cells(5,1)="Difference of Row Count"
objSummary.Cells(6,1)="Difference of Column Count"
objSummary.Cells(7,1)="False Count"
' ------------------------1st Check - Verify the position of ''Metrics' in Analysis and Reporting tab. It must be same---------------------
'Get the control of Analysis tab
Set objAnalysis=objExcel.Worksheets.Item("Analysis")
intAnalysisRowCount=objAnalysis.Usedrange.rows.count
objSummary.Cells(1,2)=intAnalysisRowCount
intAnalysisColCount=objAnalysis.Usedrange.Columns.count
objSummary.Cells(3,2)=intAnalysisColCount
'Get Column number of 'Metric' Column from Analysis tab
For intMetricAnalysis=1 to intAnalysisColCount
If(Trim(Lcase(objAnalysis.Cells(1,intMetricAnalysis)))=Trim(Lcase("Metrics"))) Then
Exit for
End If
Next
'Get all Analysis columns in 1 string
strAnalysisColumnOrder=""
For intAnalysisColumnOrder=1 to intAnalysisColCount
strAnalysisColumnOrder=strAnalysisColumnOrder&"*"&objAnalysis.Cells(1,intAnalysisColumnOrder)
If(intAnalysisColumnOrder=1) then
strAnalysisColumnOrder=Replace(strAnalysisColumnOrder,"*","")
End If
Next
Set objReporting=objExcel.Worksheets.Item("Reporting")
intReportingRowCount=objReporting.Usedrange.rows.count
objSummary.Cells(2,2)=intReportingRowCount
intReportingColCount=objReporting.Usedrange.Columns.count
objSummary.Cells(4,2)=intReportingColCount
''Get Column number of 'Metric' Column from Reporting tab
For intMetricReporting=1 to intReportingColCount
If(Trim(Lcase(objReporting.Cells(1,intMetricReporting)))=Trim(Lcase("Metrics"))) Then
Exit for
End If
Next
'Get all Reporting columns in 1 string
strReportingColumnOrder=""
For intReportingColumnOrder=1 to intAnalysisColCount
strReportingColumnOrder=strReportingColumnOrder&"*"&objReporting.Cells(1,intReportingColumnOrder)
If(intReportingColumnOrder=1) then
strReportingColumnOrder=Replace(strReportingColumnOrder,"*","")
End If
Next
''Metric' column number must be same
If(intMetricAnalysis<>intMetricReporting) then
msgbox "Merics column is at "&intMetricAnalysis&" position in 'Analysis' Tab And at "&intMetricReporting&" position in 'Reporting' tab. 'Metrics' column should be at same position in both tab."
strMetricsFlag=False
Else
strMetricsFlag=True
End IF
'-----------2nd Check, Verify count of columns in 'Analysis' And 'Reporting' tab . It Must be same
If intAnalysisColCount<>intReportingColCount Then
msgbox "Column count of 'Reporting' Tab is not same as of 'Analysis tab'."
strAnalysisColCount=False
Else
strAnalysisColCount=True
End If
''---------------3rd Check , Verify Order of columns in 'Analysis' And 'Reporting' tab . It Must be same
If Trim(Lcase(strAnalysisColumnOrder))<>Trim(Lcase(strReportingColumnOrder)) then
msgbox "Column order of 'Reporting' Tab is not same as of 'Analysis tab'. Reporting column order should be "&strAnalysisColumnOrder
strAnalysisColumnOrderFlag=False
Else
strAnalysisColumnOrderFlag=True
End IF
'Creare 'Analysis_Copy' tab and add headers
Set objAnalysisCopy=objExcel.Worksheets.Item("Analysis_Copy")
strFirstCoulmn_AggKeys=""
For intHeaderAggkey=1 to intMetricAnalysis-1
strFirstCoulmn_AggKeys=strFirstCoulmn_AggKeys&"*"&objAnalysis.Cells(1,intHeaderAggkey)
If(intHeaderAggkey=1) then
strFirstCoulmn_AggKeys=Replace(strFirstCoulmn_AggKeys,"*","")
End If
Next
objAnalysisCopy.Cells(1,1)=strFirstCoulmn_AggKeys
strSecondCoulmn_AnalysisMetrics=""
For intHeaderAnalysisMetrics=intMetricAnalysis+1 to intAnalysisColCount
strSecondCoulmn_AnalysisMetrics=strSecondCoulmn_AnalysisMetrics&"*"&objAnalysis.Cells(1,intHeaderAnalysisMetrics)
If(intHeaderAnalysisMetrics=intMetricAnalysis+1 ) then
strSecondCoulmn_AnalysisMetrics=Replace(strSecondCoulmn_AnalysisMetrics,"*","")
End If
Next
objAnalysisCopy.Cells(1,2)="Analysis_"&strSecondCoulmn_AnalysisMetrics
objAnalysisCopy.Cells(1,3)="Reporting_"&strSecondCoulmn_AnalysisMetrics
objAnalysisCopy.Cells(1,4)="Status"
objWorkbook.Save
'$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
If strAnalysisColumnOrderFlag=False OR strMetricsFlag=False OR strAnalysisColCount=False Then
msgbox "So Data Comparision can not be done"
objWorkbook.Save
objWorkbook.Close
objExcel.Quit
Else
intFalseCount=0
For intAnalysisRow=2 to intAnalysisRowCount
' ------ Get the control of ''Analysis' tab and the string of Aggrecate Keys [strAnalysisAggrData] and respective metrics [strAnalysisMetricsData]
Set objAnalysis=objExcel.Worksheets.Item("Analysis")
' Append all data of each row which is before 'Metrics' column
strAnalysisAggrData=""
For intAnalysisColumn=1 to intMetricAnalysis-1
strAnalysisAggrData=strAnalysisAggrData&"*"&objAnalysis.Cells(intAnalysisRow,intAnalysisColumn)
If(intAnalysisColumn=1) then
strAnalysisAggrData=Replace(strAnalysisAggrData,"*","")
End If
Next
' ' Append all data of each row which is after 'Metrics' column
strAnalysisMetricsData=""
For intFromMetric=intMetricAnalysis+1 to intAnalysisColCount
strAnalysisMetricsData=strAnalysisMetricsData&"*"&objAnalysis.Cells(intAnalysisRow,intFromMetric)
If(intFromMetric=intMetricAnalysis+1 ) then
strAnalysisMetricsData=Replace(strAnalysisMetricsData,"*","")
End If
Next
' ------ Get the control of ''Reporting' tab and the string of Aggrecate Keys [strAnalysisAggrData] and respective metrics [strAnalysisMetricsData]
Set objReporting=objExcel.Worksheets.Item("Reporting")
For intReportingRow=1 to intReportingRowCount
' Append all data of each row which is before 'Metrics' column
strReportingAggrData=""
For intBeforeMetricReporting=1 to intMetricReporting-1
strReportingAggrData=strReportingAggrData&"*"&objReporting.Cells(intReportingRow,intBeforeMetricReporting)
If(intBeforeMetricReporting=1) then
strReportingAggrData=Replace(strReportingAggrData,"*","")
End If
Next
' Append all data of each row which is after 'Metrics' column
strReportingMetricsData=""
For intFromReportingMetric=intMetricReporting+1 to intReportingColCount
strReportingMetricsData=strReportingMetricsData&"*"&objReporting.Cells(intReportingRow,intFromReportingMetric)
If(intFromReportingMetric=intMetricReporting+1 ) then
strReportingMetricsData=Replace(strReportingMetricsData,"*","")
End If
Next
'------------------------------------------------------------ Actual Comparision will be from here ------------------------------------------
If Trim(LCase(strAnalysisAggrData))=Trim(LCase(strReportingAggrData)) Then
objAnalysisCopy.Cells(intAnalysisRow,1)=strAnalysisAggrData
objAnalysisCopy.Cells(intAnalysisRow,2)=strAnalysisMetricsData
objAnalysisCopy.Cells(intAnalysisRow,3)=strReportingMetricsData
'Compare Metrics Data
If Trim(LCase(strAnalysisMetricsData))=Trim(LCase(strReportingMetricsData)) Then
objAnalysisCopy.Cells(intAnalysisRow,4)="PASS"
objAnalysisCopy.Cells(intAnalysisRow,4).font.color=vbGreen
Else
objAnalysisCopy.Cells(intAnalysisRow,4)="FAIL"
intFalseCount=intFalseCount+1
objAnalysisCopy.Cells(intAnalysisRow,4).font.color=vbRed
End If
Exit For
End If
Next
Next
objSummary.Cells(5,2)=intAnalysisRowCount-intReportingRowCount
objSummary.Cells(6,2)=intAnalysisColCount-intReportingColCount
objSummary.Cells(7,2)=intFalseCount
objSummary.Cells(7,2).font.color=vbRed
objWorkbook.Save
objWorkbook.Close
objExcel.Quit
EndTime=Timer()
TotalTime=EndTime-startTime
msgbox "Data Comparision is Completed. Comparision time is "&TotalTime&"Secs"
End If
Use a dictionary and you avoid the nested loops and only scan each sheet once. For example as a VBA macro (untested)
Sub compare()
Dim wb As Workbook
Dim ws(2) As Worksheet, wsSum As Worksheet, wsCopy As Worksheet
Dim rowCount(2) As Long, colCount(2) As Integer, colMetric(2) As Integer
Dim colsMetric(2) As String, colsAll(2) As String, colsKeys(2) As String
Dim bMetricsFlag As Boolean, bColCountFlag As Boolean, bColOrderFlag As Boolean
Dim i As Long, ar, msg As String, intFalseCount As Long
Dim t0 as Single
t0 = Timer
Set wb = ThisWorkbook
Set ws(1) = wb.Sheets("Analysis")
Set ws(2) = wb.Sheets("Reporting")
Set wsSum = wb.Sheets("Summary")
wsSum.Cells.Clear
wsSum.Range("A1:A7") = WorksheetFunction.Transpose(Array("Analysis Row Count", _
"Reporting Row Count", "Analysis Column Count", "Reporting Column Count", _
"Difference of Row Count", "Difference of Column Count", "False Count"))
Set wsCopy = wb.Sheets("Analysis_Copy")
wsCopy.Cells.Clear
' get stats for each sheet 1-Analyis 2=Reporting
For i = 1 To 2
ar = Stats(ws(i))
rowCount(i) = ar(0)
colCount(i) = ar(1)
colMetric(i) = ar(2)
colsAll(i) = ar(3)
colsMetric(i) = ar(4)
colsKeys(i) = ar(5)
Next
' summary
With wsSum
.Cells(1, 2) = rowCount(1)
.Cells(2, 2) = rowCount(2)
.Cells(3, 2) = colCount(1)
.Cells(4, 2) = colCount(2)
End With
' check stats
'Metric' column number must be same
If colMetric(1) = 0 Or colMetric(2) = 0 Or colMetric(1) <> colMetric(2) Then
msg = "Metrics columns not the same or missing : " & vbCr & _
"Analysis : " & colMetric(1) & vbCr & _
"Reporting : " & colMetric(2)
MsgBox msg, vbCritical
bMetricsFlag = False
Else
bMetricsFlag = True
End If
' Verify count of columns
If colCount(1) <> colCount(2) Then
msg = "Column counts not the same : " & vbCr & _
"Analysis : " & colCount(1) & vbCr & _
"Reporting : " & colCount(2)
MsgBox msg, vbCritical
bColCountFlag = False
Else
bColCountFlag = True
End If
'Verify Order of columns
If colsAll(1) <> colsAll(2) Then
msg = "Column order not the same : " & vbCr & _
"Analysis : " & colsAll(1) & vbCr & _
"Reporting : " & colsAll(2)
MsgBox msg, vbCritical
bColOrderFlag = False
Else
bColOrderFlag = True
End If
With wsCopy
.Cells(1, 1) = colsKeys(1)
.Cells(1, 2) = "Analysis_" & colsMetric(1)
.Cells(1, 3) = "Reporting_" & colsMetric(2)
.Cells(1, 4) = "Status"
End With
' checks OK ?
If bColOrderFlag And bMetricsFlag And bColCountFlag Then
' ok
Else
MsgBox "So Data Comparision can not be done", vbCritical
Exit Sub
End If
' start comparison
Dim dict As Object, m As Long, c As Long, s As String
Dim sKey As String, sMetric As String
Set dict = CreateObject("Scripting.Dictionary")
' scan Reporting sheet to build dictionary
m = colMetric(2)
For i = 1 To rowCount(2)
'join cols up to and after metric col
sMetric = "": sKey = ""
For c = 1 To colCount(2)
s = Trim(ws(2).Cells(i, c))
If c < m Then
If sMetric <> "" Then sMetric = sMetric & "*"
sMetric = sMetric & s
ElseIf c > m Then
If sKey <> "" Then sKey = sKey & "*"
sKey = sKey & s
End If
Next
dict(sKey) = sMetric
Next
' scan Analysis sheet to compare dictionary
m = colMetric(1)
For i = 2 To rowCount(1)
'join cols up to and after metric col
sMetric = "": sKey = ""
For c = 1 To colCount(1)
s = Trim(ws(1).Cells(i, c))
If c < m Then
If sMetric <> "" Then sMetric = sMetric & "*"
sMetric = sMetric & s
ElseIf c > m Then
If sKey <> "" Then sKey = sKey & "*"
sKey = sKey & s
End If
Next
' result
wsCopy.Cells(i, 1) = sKey
wsCopy.Cells(i, 2) = sMetric
wsCopy.Cells(i, 3) = dict(sKey)
' pass or fail
If sMetric = dict(sKey) Then
wsCopy.Cells(i, 4) = "PASS"
wsCopy.Cells(i, 4).Font.Color = vbGreen
Else
wsCopy.Cells(i, 4) = "FAIL"
wsCopy.Cells(i, 4).Font.Color = vbRed
intFalseCount = intFalseCount + 1
End If
Next
With wsSum
.Cells(5, 2) = rowCount(1) - rowCount(2)
.Cells(6, 2) = colCount(1) - colCount(2)
.Cells(7, 2) = intFalseCount
.Cells(7, 2).Font.Color = vbRed
End With
MsgBox i - 2 & " rows scanned " & vbCrLf & _
intFalseCount & " FAILED", vbInformation, Int(Timer - t0) & "seconds"
End Sub
Function Stats(ws As Worksheet) As Variant
Dim c As Integer, ar(5) As Variant, s As String
ar(0) = ws.UsedRange.Rows.Count
ar(1) = ws.UsedRange.Columns.Count
ar(2) = 0 'metric column
ar(3) = "" ' col aggregated
ar(4) = "" ' cols upto not including metric
ar(5) = "" ' cols after metric
For c = 1 To ar(1)
s = LCase(Trim(ws.Cells(1, c)))
If s = "metric" Then
ar(2) = c
End If
' aggregate headers before/after metric
If ar(2) = 0 Then
If ar(4) <> "" Then ar(4) = ar(4) & "*"
ar(4) = ar(4) & s
ElseIf c > ar(2) Then
If ar(5) <> "" Then ar(5) = ar(5) & "*"
ar(5) = ar(5) & s
End If
' aggregate all
If ar(3) <> "" Then ar(3) = ar(3) & "*"
ar(3) = ar(3) & s
Next
Stats = ar
End Function
Test data generator
Sub testdata()
Dim ws As Worksheet, n, r, c, ar
ar = Array("", "Analysis", "Reporting")
For n = 1 To 2
Set ws = Sheets(ar(n))
For r = 1 To 30000
For c = 1 To 15
ws.Cells(r, c) = Chr(64 + c) & r & "_abcdefghijklmnopqrstuvwxyz_"
Next
Next
ws.Cells(1, 10) = "metric" ' col J
Next
MsgBox "test data created"
End Sub

Any way I can speed up this sub procedure?

I figure there must be a way to drastically shorten this sub procedure. The reason why I even want to is because it's freezing up the application on the elseif userform1.optTerm line because that worksheet it pulls the data from is 6x longer in rows than the optInSeat list.
Sub LoadEmployee_Cmb_HC()
Dim isWS As Worksheet: Set isWS = ThisWorkbook.Worksheets("In Seat")
Dim tWs As Worksheet: Set tWs = ThisWorkbook.Worksheets("Terms")
Dim a, b As Long, c As Variant, i As Long
If UserForm1.optInSeat = True Then
If UserForm1.optEmployeeName = True Then
For i = 2 To isWS.Cells(Rows.Count, 4).End(xlUp).row
x = Application.CountIf(isWS.Range("D" & 4, "D" & i), _
isWS.Cells(i, 4).Value)
If x = 1 Then
UserForm1.cmbEmployee.AddItem isWS.Cells(i, 4) & " - " & isWS.Cells(i, 1)
End If
Next i
For a = 0 To UserForm1.cmbEmployee.ListCount - 1
For b = 0 To UserForm1.cmbEmployee.ListCount - 1
If UserForm1.cmbEmployee.List(b) > UserForm1.cmbEmployee.List(a) Then
c = UserForm1.cmbEmployee.List(a)
UserForm1.cmbEmployee.List(a) = UserForm1.cmbEmployee.List(b)
UserForm1.cmbEmployee.List(b) = c
End If
Next
Next
ElseIf UserForm1.optEmployeeID = True Then
For i = 2 To isWS.Cells(Rows.Count, 1).End(xlUp).row
x = Application.CountIf(isWS.Range("A" & 1, "A" & i), _
isWS.Cells(i, 1).Value)
If x = 1 Then
UserForm1.cmbEmployee.AddItem isWS.Cells(i, 1) & " - " & isWS.Cells(i, 4)
End If
Next i
For a = 0 To UserForm1.cmbEmployee.ListCount - 1
For b = 0 To UserForm1.cmbEmployee.ListCount - 1
If UserForm1.cmbEmployee.List(b) > UserForm1.cmbEmployee.List(a) Then
c = UserForm1.cmbEmployee.List(a)
UserForm1.cmbEmployee.List(a) = UserForm1.cmbEmployee.List(b)
UserForm1.cmbEmployee.List(b) = c
End If
Next
Next
End If
ElseIf UserForm1.optTerm = True Then
If UserForm1.optEmployeeName = True Then
For i = 2 To tWs.Cells(Rows.Count, 4).End(xlUp).row
x = Application.CountIf(tWs.Range("D" & 4, "D" & i), _
tWs.Cells(i, 4).Value)
If x = 1 Then
UserForm1.cmbEmployee.AddItem tWs.Cells(i, 4) & " - " & tWs.Cells(i, 1)
End If
Next i
For a = 0 To UserForm1.cmbEmployee.ListCount - 1
For b = 0 To UserForm1.cmbEmployee.ListCount - 1
If UserForm1.cmbEmployee.List(b) > UserForm1.cmbEmployee.List(a) Then
c = UserForm1.cmbEmployee.List(a)
UserForm1.cmbEmployee.List(a) = UserForm1.cmbEmployee.List(b)
UserForm1.cmbEmployee.List(b) = c
End If
Next
Next
ElseIf UserForm1.optEmployeeID = True Then
For i = 2 To tWs.Cells(Rows.Count, 1).End(xlUp).row
x = Application.CountIf(tWs.Range("A" & 1, "A" & i), _
tWs.Cells(i, 1).Value)
If x = 1 Then
UserForm1.cmbEmployee.AddItem tWs.Cells(i, 1) & " - " & tWs.Cells(i, 4)
End If
Next i
For a = 0 To UserForm1.cmbEmployee.ListCount - 1
For b = 0 To UserForm1.cmbEmployee.ListCount - 1
If UserForm1.cmbEmployee.List(b) > UserForm1.cmbEmployee.List(a) Then
c = UserForm1.cmbEmployee.List(a)
UserForm1.cmbEmployee.List(a) = UserForm1.cmbEmployee.List(b)
UserForm1.cmbEmployee.List(b) = c
End If
Next
Next
End If
End If
End Sub
Instead of trying to shape the data using code, I would suggest creating an SQL statement based on runtime logic, opening a recordset with that data, and pushing the result back into the combobox.
Add a reference (Tools -> References...) to Microsoft ActiveX Data Objects; the latest version, usually 6.1.
(Credit goes to CDP1802's answer, which is the basis for much of the logic here.)
Dim source As String
If optInSeat = True Then
source = "'In Seat$'"
ElseIf optTerm = True Then
source = "Terms$"
End If
If Len(source) = 0 Then Exit Sub ' Do nothing
' sort by columns
Dim orderBy As String, expr As String
If optEmployeeName Then
expr = "Trim(F1) & ' - ' & Trim(F4)"
orderBy = "F1, F4"
ElseIf optEmployeeID Then
expr = "Trim(F4) & ' - ' & Trim(F1)"
orderBy = "F4, F1"
Else
expr = "Trim(F1) & ' - ' & Trim(F4)"
End If
Dim connectionString As String
connectionString = _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=""" & ThisWorkbook.FullName & """;" & _
"Extended Properties=""Excel 12.0;HDR=No"""
Dim sql As String
sql = _
"SELECT " & expr & " " & _
"FROM [" & source & "]"
If Len(orderBy) > 0 Then sql = sql & " ORDER BY " & orderBy
Dim rs As New ADODB.Recordset
rs.Open sql, connectionString
' The 2D array comes back in the wrong direction to be set directly.
' We use WorksheetFunctions.Transpose to switch the direction.
cmbEmployees.List = WorksheetFunction.Transpose(rs.GetRows)
Select unique items using a Dictionary Object and sort them in an array. This sorts in ascending order.
Sub LoadEmployee_Cmb_HC()
Dim wb As Workbook, ws As Worksheet
Dim dict, k As String, i As Long
Dim order(2) As Integer
Set wb = ThisWorkbook
Set dict = CreateObject("Scripting.Dictionary")
' data source
If UserForm1.optInSeat = True Then
Set ws = wb.Sheets("In Seat")
ElseIf UserForm1.optTerm = True Then
Set ws = wb.Sheets("Terms")
End If
' sort by columns
If UserForm1.optEmployeeName = True Then
order(1) = 4: order(2) = 1
ElseIf UserForm1.optEmployeeID = True Then
order(1) = 1: order(2) = 4
End If
If order(1) = 0 Or ws Is Nothing Then
' do nothing
Else
' get unique values start in row 4
For i = 4 To ws.Cells(Rows.Count, order(1)).End(xlUp).Row
k = Trim(ws.Cells(i, order(1)).Value)
If Len(k) > 0 And Not dict.exists(k) Then
dict.Add k, k & " - " & Trim(ws.Cells(i, order(2)))
End If
Next
' sort and populate combo
Call SortCombo(dict, UserForm1.ComboBox1)
End If
End Sub
Sub SortCombo(ByRef dict, cmb As ComboBox)
Dim ar, a As Long, b As Long, i As Long, tmp As String
ar = dict.keys
i = UBound(ar)
For a = 0 To i
For b = a To i
If ar(b) < ar(a) Then
tmp = ar(a)
ar(a) = ar(b)
ar(b) = tmp
End If
Next
ar(a) = dict.Item(ar(a)) ' replace with value after it sort
Next
cmb.List = ar
End Sub
Alternative sort using temporary sheet
Sub SortCombo2(ByRef dict, cmb As ComboBox)
Dim wsTmp As Worksheet, rng As Range, k, ar() As String, i As Long
Set wsTmp = ThisWorkbook.Sheets(3)
wsTmp.Cells.Clear
ReDim ar(dict.Count - 1, 0)
i = 0
For Each k In dict.keys
ar(i, 0) = dict(k)
i = i + 1
Next
Set rng = wsTmp.Range("A1:A" & dict.Count)
rng = ar
With wsTmp.Sort
.SetRange rng
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.Apply
End With
cmb.List = rng.Value2
wsTmp.Cells.Clear
End Sub
Test data generator
Sub data()
Dim ws As Worksheet, i, s, n
Set ws = Sheets("Terms")
ws.Cells.Clear
For i = 4 To 35000
s = ""
For n = 1 To 25
s = s & Chr(65 + Int(Rnd() * 26))
Next
ws.Cells(i, 1) = s
ws.Cells(i, 4) = "D" & i
Next
MsgBox "done " & i - 1
End Sub

Excel VBA SUMIF Super slow code

I have SUMIF running really really slow. My data has 14,800 Rows and 39 Columns.
I do the following:
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
EDITED TO ADD more potentially relevant code that may be interacting with the SUMIF command
It may be relevant to the speed issue so I'll mention it. I get the user to open a file from wherever they may have stored the report. The file then stays open. Maybe that is a problem. I don't know if it should be some other way.. like I close it but keep the address in mind or something??
FilterType = "Text Files (*.txt),*.txt," & "Comma Separated Files (*.csv),*.csv," & "ASCII Files (*.asc),*.asc," & "All Files (*.*),*.*"
FilterIndex = 4
Title = "File to be Selected"
File_path = Application.GetOpenFilename(FileFilter:=FilterType, FilterIndex:=FilterIndex, Title:=Title)
If File_path = "" Then
MsgBox "No file was selected."
Exit Sub
End If
Set wbSource = Workbooks.Open(File_path)
Original_Name = ActiveWorkbook.Name
If ActiveSheet.AutoFilterMode Then
ActiveSheet.AutoFilterMode = False
End If
Set wb1 = ActiveWorkbook
Set ws1 = wb1.Worksheets("Sheet1")
With ws1
FinalColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
FinalRow = .Range("B" & .Rows.Count).End(xlUp).Row
For j = 1 To FinalColumn
If .Cells(1, j).Value = "Effec.Date" Then
Effective_Date_Column = j
ElseIf .Cells(1, j).Value = "FolderId" Then
FolderId_column = j
ElseIf .Cells(1, j).Value = "FolderNotional" Then
FolderNotional_column = j
End If
Next j
'range_Total_Folder_Fixed = .Cells(2, Total_Folder_Column).Address & ":" & .Cells(FinalRow, Total_Folder_Column).Address
range_FolderId_Fixed = .Cells(2, FolderId_column).Address & ":" & .Cells(FinalRow, FolderId_column).Address
range_FolderId_Cell = .Cells(2, FolderId_column).Address(RowAbsolute:=False, ColumnAbsolute:=False)
range_FolderNotional_Fixed = .Cells(2, FolderNotional_column).Address & ":" & .Cells(FinalRow, FolderNotional_column).Address
Everything runs in 8-10 seconds until we come to the lie below. Now the total time jumps to a 150 seconds.
.Range(range_Total_Folder_Fixed).Formula = "=SUMIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "," & range_FolderNotional_Fixed & ")"
Am I doing something wrong? Is there a better (more efficient) way to write a general formula?
EDIT: Code generated Raw Formula
Some of the excel worksheet functions in my code:
.Range(range_Isnumber).Formula = "=(RIGHT(" & range_TradeId_cell & ",2)<> ""IB"")*1"
.Range(range_Is_IB).Formula = "=(RIGHT(" & range_TradeId_cell & ",2)= ""IB"")*1"
.Range(range_Exceptions).Formula = "=(SUMIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "," & range_Isnumber_fixed & ")= COUNTIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "))*1+(SUMIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "," & range_Is_IB_fixed & ")= COUNTIF(" & range_FolderId_Fixed & "," & range_FolderId_Cell & "))*1 "
.Range("C13").FormulaR1C1 = "=SUM(IF(FREQUENCY(MATCH([SCL_FL_2013_11_30.xlsx]Rest!R2C2:R2321C2,[SCL_FL_2013_11_30.xlsx]Rest!R2C2:R2321C2,0),MATCH([SCL_FL_2013_11_30.xlsx]Rest!R2C2:R2321C2,[SCL_FL_2013_11_30.xlsx]Rest!R2C2:R2321C2,0))>0,1))"
So Stuff like
Range("I2")=SUMIF($H$2:$H$5,H2,$G$2:$G$5)
Where the data could be like
RowG RowH RowI
Alice 1 4
Alice 3 4
Bob 9 17
Bob 8 17
Dan 2 2
EDIT2 : Implementing Sam's solution, I am getting errors:
Set range_FolderId_Fixed = .Range(.Cells(2, FolderId_column), .Cells(FinalRow, FolderId_column))
Set range_FolderId_Cell = .Range(.Cells(2, FolderId_column),.Cells(FinalRow, FolderId_column))
Set range_FolderNotional_Fixed = .Range(.Cells(2, FolderNotional_column), .Cells(FinalRow, FolderNotional_column))
Set range_Total_Folder_Fixed = .Range(.Cells(2, Total_Folder_Column), .Cells(FinalRow, Total_Folder_Column))
.Range(range_Total_Folder_Fixed).Value = SumIf_func(range_FolderId_Fixed, range_FolderId_Cell, range_FolderNotional_Fixed)
I am getting a type application defined or object defined error in the line below.
.Range(range_Total_Folder_Fixed).Value = SumIf_func(range_FolderId_Fixed, range_FolderId_Cell, range_FolderNotional_Fixed)
I have no idea what to do next.
Ok this is what I came up with
Public Function SumIf_func(rng As Range, _
criteria As Range, _
sumRange As Range) As Variant()
Dim rngArr() As Variant
Dim sumArr() As Variant
Dim criteriaArr() As Variant
Dim returnArr() As Variant
Dim temp As Double
rngArr = rng.Value2
sumArr = sumRange.Value2
criteriaArr = criteria.Value2
If UBound(sumArr) <> UBound(rngArr) Then _
Err.Raise 12345, "SumIf_func", "Sum range and check range should be the same size"
If UBound(sumArr, 2) <> 1 Or UBound(rngArr, 2) <> 1 Then _
Err.Raise 12346, "SumIf_func", "Sum range and check range should be a single column"
ReDim returnArr(1 To UBound(criteriaArr), 1 To 1)
For c = LBound(criteriaArr) To UBound(criteriaArr)
returnArr(c, 1) = Application.WorksheetFunction.SumIf(rng, criteriaArr(c, 1), sumRange)
Next c
SumIf_func = returnArr
End Function
This function takes in three ranges:
The range to check
The range where the criteria are
The range where the values to sum are
The range to check and the sum range should both be the same length and only be 1 column across.
The array that is returned will be the same size as the criteria array..
Here is an example of usage:
Public Sub test_SumIf()
Dim ws As Worksheet
Set ws = Sheet1
Dim rng As Range, sumRng As Range, criteria As Range
Set rng = ws.Range("A1:A100")
Set sumRng = ws.Range("B1:B100")
Set criteria = ws.Range("C1:C10")
ws.Range("D1:D10").Value = SumIf_func(rng, criteria, sumRng)
End Sub

Resources