Speed up of SumIf between two arrays (13k; 30) & (500, 30) based on double criteria - array loop

d3rowy

New Member
Joined
Mar 29, 2023
Messages
6
Office Version
  1. 365
Platform
  1. Windows
Dear MrExcel Lads,

I'm looking for a way to speed up my macro. Current timing varies between 30s if array2 has 500 rows and 2minutes if array2 contains 1500rows.

Case:
- two arrays: arrayInput & arrayAdjustments
- arrayAdjustment is created from a template in one of the sheets and then filled with data by week/keyfigure
- product & keyfigure combinatation is not unique in arrayinput
- product & keyfigure combination is unique in arrayAdjusments
- there are more types of keyfigures in arrayInput than in arrayAdjusments, im interested only in summing up what is in arrayAdjustment
- arrayAdjustments is basically a pivot with additional filtering of keyfigure, but it need it as raw table to process and change later

My current approach is to do it via array loops, is there a faster way? Or a way to optimize my code?
(I'm using a couple of public functions in the code below, but hope the their name will be self explanatory)

VBA Code:
Sub adjSheetFill()


Dim timerAdj_fast As Single
timerAdj_fast = Timer()

Call sharedData.defineVariables

Dim numberColumnKeyFigure As Integer
Dim arrayInput As Variant, arrayKF As Variant, arrayAdjustment As Variant, result As Variant
Dim keyFigure As String, keyFigureInput As String, product As String
Dim rng As range, cell As range, sum As Double
Dim columnKeyFigureAdjustment As Integer, columnkeyFigureInput As Integer, phasing As Integer


arrayAdjustment = Worksheets(sheetAdjustments).range("A1").CurrentRegion.Value       'array with list of desired KF from workspace tab,

Dim r As Long, j As Long, i as Long

            arrayInput = createarrayInput
            arrayInput = addKeyColumnToArray(arrayInput)
            columnkeyFigureInput = findHeaderinArray(arrayInput, headerKeyFigures)
            columnKeyFigureAdjustment = findHeaderinArray(arrayAdjustment, headerKeyFigures)
            columnProductAdjustment = findHeaderinArray(arrayAdjustment, headerKey)
            phasing = columnkeyFigureInput - columnKeyFigureAdjustment

                      
    For r = LBound(arrayAdjustment, 1) + 1 To UBound(arrayAdjustment, 1)
            sum = 0

            keyFigure = arrayAdjustment(r, columnKeyFigureAdjustment)
            product = arrayAdjustment(r, columnProductAdjustment)

            For j = columnKeyFigureAdjustment + 1 To columnKeyFigureAdjustment + periodCountWeeks

            sum = 0

                For i = LBound(arrayInput, 1) + 1 To UBound(arrayInput, 1) - 1
                    If IsEmpty(arrayInput(i, columnkeyFigureInput)) Then
                    GoTo continue
                    End If

                sum = sum + arrayInput(i, j + phasing)
                arrayAdjustment(r, j) = sum

                Next i             

continue:

                sum = sum + arrayInput(i, j + phasing)
                arrayAdjustment(r, j) = sum

            Next j

    Next r


Worksheets(sheetAdjustment.range("A1:AD10000").ClearContents
Call pasteArray(arrayAdjustment, sheetAdjustment, "A1", 0)

Debug.Print "adjSheetFill_fast - end : " & Timer - timerAdj_fast

End Sub


Book1
ABCDEFGHIJKLMNOPQRSTUVW
1Product DescKey FigureW02 2023W03 2023W04 2023W05 2023W06 2023W07 2023W08 2023W09 2023W10 2023W11 2023W12 2023W13 2023W14 2023W15 2023W16 2023W17 2023W18 2023W19 2023W20 2023W21 2023W22 2023
2AppleFactor 13026.2526.2522.522.526.2522.522.522.522.522.522.522.522.522.522.522.522.522.522.522.5
3AppleFactor 23026.2526.2522.522.526.2522.522.522.522.522.522.522.522.522.522.522.522.522.522.522.5
4AppleStat Factor000000000000000000000
5AppleAdjusted Stat Factor25.59139625.59139625.59139627.54992328.33333528.33333528.33333528.18676231.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.141869
6AppleBaseline25.59139625.59139625.59139627.54992328.33333528.33333528.33333528.18676231.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.141869
7ApplePromotion
8AppleSales
9AppleSales Plan25.59139625.59139625.59139627.54992328.33333528.33333528.33333528.18676231.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.141869
10AppleSales Adjustments
11AppleDemand-12.172228-12.172228-12.172228-8.264976-8.5-8.5
12AppleConsensus Demand13.41916813.41916813.41916819.28494719.83333519.83333528.33333528.18676231.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.141869
13AppleCustomer Demand
14AppleTotal Demand4545454531.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.14186931.141869
15AppleActuals45454545
16AppleStock
17BananaFactor 14541.2537.533.7533.7537.53026.2545454545454545454545454545
18BananaFactor 24541.2537.533.7533.7537.53026.2545454545454545454545454545
19BananaStat Factor000000000000000000000
20BananaAdjusted Stat Factor42.4179542.4179544.38356344.38356344.38356344.38356344.38356335.536238.29787339.01041839.01041839.01041739.01041839.01041839.01041839.01041839.01041839.01041839.01041839.01041839.010417
21BananaBaseline42.4179542.4179544.38356344.38356344.38356344.38356344.38356335.536238.29787339.01041839.01041839.01041739.01041839.01041839.01041839.01041839.01041839.01041839.01041839.01041839.010417
22BananaPromotion
23BananaSales
24BananaSales Plan42.4179542.4179544.38356344.38356344.38356344.38356344.38356335.536238.29787339.01041839.01041839.01041739.01041839.01041839.01041839.01041839.01041839.01041839.01041839.01041839.010417
25BananaSales Adjustments
26BananaDemand0000
27BananaConsensus Demand42.4179542.4179544.38356344.38356344.38356344.38356344.38356335.536238.29787339.01041839.01041839.01041739.01041839.01041839.01041839.01041839.01041839.01041839.01041839.01041839.010417
28BananaCustomer Demand
29BananaTotal Demand45459022539.01041839.01041739.01041839.01041839.01041839.01041839.01041839.01041839.01041839.01041839.010417
30BananaActuals454590225
31BananaStock
32AppleFactor 1180165165172.5168.7590108.75127.5131.25135135135135135135135135135135135135
33AppleFactor 2180165165172.5168.7590108.75127.5131.25135135135135135135135135135135135135
34AppleStat Factor000000000000000000000
35AppleAdjusted Stat Factor155.806438155.806437179.572448179.572448179.572448179.572448179.572448150.562641188.558469188.558469188.558469188.558468188.558469188.558469188.558469188.558469188.558469188.558469188.558469188.558469188.558469
36AppleBaseline155.806438155.806437179.572448179.572448179.572448179.572448179.572448150.562641188.558469188.558469188.558469188.558468188.558469188.558469188.558469188.558469188.558469188.558469188.558469188.558469188.558469
37ApplePromotion
38AppleSales
39AppleSales Plan155.806438155.806437179.572448179.572448179.572448179.572448179.572448150.562641188.558469188.558469188.558469188.558468188.558469188.558469188.558469188.558469188.558469188.558469188.558469188.558469188.558469
40AppleSales Adjustments
41AppleDemand00-53.871734-53.871734-53.871734-53.871734
42AppleConsensus Demand155.806438155.806437125.700714125.700714125.700714125.700714179.572448150.562641188.558469188.558469188.558469188.558468188.558469188.558469188.558469188.558469188.558469188.558469188.558469188.558469188.558469
43AppleCustomer Demand0000
44AppleTotal Demand42.4179542.4179544.38356344.38356344.38356344.38356344.38356335.536238.29787339.01041839.01041839.01041739.01041839.01041839.01041839.01041839.01041839.01041839.01041839.01041839.010417
45AppleActuals
46AppleStock45459022539.01041839.01041739.01041839.01041839.01041839.01041839.01041839.01041839.01041839.01041839.010417
arrayInput


Book1
ABCDEFGHIJKLMNOPQRSTUVW
16Product DescKey FigureSum of W02 2023Sum of W03 2023Sum of W04 2023Sum of W05 2023Sum of W06 2023Sum of W07 2023Sum of W08 2023Sum of W09 2023Sum of W10 2023Sum of W11 2023Sum of W12 2023Sum of W13 2023Sum of W14 2023Sum of W15 2023Sum of W16 2023Sum of W17 2023Sum of W18 2023Sum of W19 2023Sum of W20 2023Sum of W21 2023Sum of W22 2023
17AppleActuals
18AppleDemand
19ApplePromotion
20AppleSales
21BananaActuals
22BananaDemand
23BananaPromotion
24BananaSales
arrayAdjustmentsEmpty





Book1
ABCDEFGHIJKLMNOPQRSTUVW
16Product DescKey FigureSum of W02 2023Sum of W03 2023Sum of W04 2023Sum of W05 2023Sum of W06 2023Sum of W07 2023Sum of W08 2023Sum of W09 2023Sum of W10 2023Sum of W11 2023Sum of W12 2023Sum of W13 2023Sum of W14 2023Sum of W15 2023Sum of W16 2023Sum of W17 2023Sum of W18 2023Sum of W19 2023Sum of W20 2023Sum of W21 2023Sum of W22 2023
17AppleActuals45454545
18AppleDemand-12.172228-12.172228-66.043962-62.13671-62.371734-62.371734
19ApplePromotion
20AppleSales
21BananaActuals454590225
22BananaDemand0000
23BananaPromotion
24BananaSales
arrayAdjustmentsFilled
 

Excel Facts

How to create a cell-sized chart?
Tiny charts, called Sparklines, were added to Excel 2010. Look for Sparklines on the Insert tab.
This may take a few goes to get right, but here's a start. Try the following code on a copy of your file.

VBA Code:
Option Explicit
Sub Multi_Keys_Multi_Items()
    Dim t As Double: t = Timer
    Dim rng As Range, R As Range, txt As String
    Dim i As Long, j As Long, n As Long, ar
    
    Dim ws1 As Worksheet, ws2 As Worksheet
    Set ws1 = Worksheets("arrayInput")      '<~~~ Check names of source & destination sheets carefully
    Set ws2 = Worksheets("arrayAdjustments")
    
    Set rng = ws1.Range("A1:A" & ws1.Cells(Rows.Count, "A").End(xlUp).Row)
    ar = ws1.Range("A1").CurrentRegion
    
    With CreateObject("scripting.dictionary")
        For Each R In rng
            txt = R.Value2 & "|" & R.Offset(0, 1).Value
            If Not .exists(txt) Then
                n = n + 1
                .Add txt, n
                For j = 1 To UBound(ar, 2)
                    ar(n, j) = R.Offset(, j - 1)
                Next j
            Else
                For i = 3 To UBound(ar, 2)
                    ar(.Item(txt), i) = ar(.Item(txt), i) + R.Offset(, i - 1)
                Next i
            End If
        Next R
    End With
    ws2.Range("A1").CurrentRegion.Offset(1).ClearContents
    ws2.Range("A1").Resize(n, UBound(ar, 2)) = ar
    MsgBox "adjSheetFill_fast - end : " & Timer - t & " seconds."
End Sub
 
Upvote 0
Hi Kevin, thank you for posting your code. I will try it today :)
 
Upvote 0
Thanks you for that (y) Looking back at my post, I think I should have explained a bit better how the code actually works. As you probably gathered, it uses a Dictionary approach rather than arrays (dictionaries are brilliant for ultra fast sumifs etc.). As a test, put all your data in your arrayInput sheet on one sheet, and have a second sheet in the same workbook (completely blank) called arrayAdjustments. I tested it on 3K rows and it completed in under 0.5 seconds. I have made a couple of improvements to the code I posted earlier, if you'd like to try this instead?

VBA Code:
Option Explicit
Sub Multi_Keys_Multi_Items_V2()
    Dim t As Double: t = Timer
    Dim rng As Range, R As Range, txt As String
    Dim i As Long, j As Long, n As Long, ar
    
    Dim ws1 As Worksheet, ws2 As Worksheet
    Set ws1 = Worksheets("arrayInput")      '<~~~ Check names of source & destination sheets carefully
    Set ws2 = Worksheets("arrayAdjustments")
    
    Set rng = ws1.Range("A1:A" & ws1.Cells(Rows.Count, "A").End(xlUp).Row)
    ar = ws1.Range("A1").CurrentRegion
    
    With CreateObject("scripting.dictionary")
        For Each R In rng
            txt = R.Value2 & "|" & R.Offset(0, 1).Value
            If Not .exists(txt) Then
                n = n + 1
                .Add txt, n
                For j = 1 To UBound(ar, 2)
                    ar(n, j) = R.Offset(, j - 1)
                Next j
            Else
                For i = 3 To UBound(ar, 2)
                    ar(.Item(txt), i) = ar(.Item(txt), i) + R.Offset(, i - 1)
                Next i
            End If
        Next R
    End With
    ws2.Range("A1").CurrentRegion.Offset(1).ClearContents
    ws2.Range("A1").Resize(n, UBound(ar, 2)) = ar
    
    With ws2.Range(ws2.Cells(2, 3), ws2.Cells(Cells.Find("*", , xlFormulas, , 1, 2).Row, ws2.Cells(1, Columns.Count).End(xlToLeft).Column))
        .Replace 0, "", 1
    End With
    
    For i = 3 To ws2.Cells(1, Columns.Count).End(xlToLeft).Column
        ws2.Cells(1, i) = "Sum of " & ws2.Cells(1, i)
    Next i
    ws2.UsedRange.EntireColumn.AutoFit
    MsgBox "adjSheetFill_fast - end : " & Timer - t & " seconds."
End Sub
 
Upvote 0
Solution
I managed to get it partially going and it looks very promising <2s for 13k rows! Thank you :). What I'm maybe missing or do not understand is the content or "ar" tail rows.
"ar" is initially declared as whole content of "arrayInut" ~13k rows, then code is looping through looking for unique key, if its there then it effectively overwriting given row of "ar" with what it found. If its not unique it will sumup all the data and put it in again in given row of "ar", this is clear. But if we will find only 500 unique keys (txt), wont it overwrite only first 500 rows and we are still left with 12.5k tail rows in the same array?
 
Upvote 0
I'm not following you 100%. The array 'ar' is put into the second sheet ("arrayAdjustments" or "ws2" in my code) after that second sheet is cleared of all its data first. The data in the first sheet ("arrayInput" or "ws1" in my code) shouldn't be affected at all. Is this not quite what you were after?
 
Upvote 0
This is exactly what we are after, yes.

By using this piece, we are putting whole arrayInput sheet, in my case all 13k rows into array "ar".

VBA Code:
    Set ws1 = Worksheets("arrayInput")      '<~~~ Check names of source & destination sheets carefully
    Set rng = ws1.Range("A1:A" & ws1.Cells(Rows.Count, "A").End(xlUp).Row)
    ar = ws1.Range("A1").CurrentRegion

Then we proceed to overwrite some of ar values, based on dictionary entries:

VBA Code:
 If Not .exists(txt) Then
                n = n + 1
                .Add txt, n
                For j = 1 To UBound(ar, 2)
                    ar(n, j) = R.Offset(, j - 1)
                Next j
            Else
                For i = 3 To UBound(ar, 2)
                    ar(.Item(txt), i) = ar(.Item(txt), i) + R.Offset(, i - 1)
                Next i
            End If

But n will never reach 13k, as there is a lot of duplicated entries. Product & key figure combination in arrayInput is not unique. We overwrite only n amount of rows in "ar" and are left with 13k - n that originally came from the 1st code snippet. I'm trying to understand if this is the case, or im missing smth in process.
If this is truly the case then solution is quite easy - cutting rows from ar / putting data in other array
 
Upvote 0
Then we proceed to overwrite some of ar values, based on dictionary entries:
Not quite, you don't actually "overwrite" any values - you use the array "ar" to build the Dictionary based on unique entries only.
We overwrite only n amount of rows in "ar" and are left with 13k - n that originally came from the 1st code snippet. I'm trying to understand if this is the case, or im missing smth in process.
Again, you don't actually "overwrite" anything. If the product+key combination have already been added to the dictionary, then the values are added to what already exists against that product-key combination - effectively doing a Sumifs(). You're not actually left with "13k - n" as you put it, you're left with n.
If this is truly the case then solution is quite easy - cutting rows from ar / putting data in other array
Don't follow this bit. Is this a question, or a proposed "solution" to a problem? If so, what is the problem? Or am I just getting tired (been a long day :sleep:)
 
Upvote 0
Thank you for all the help, please get some rest :) As I though, I'm misunderstanding what the code is doing, but I will get there.
You help me tremendously, once again - thank you!
 
Upvote 0

Forum statistics

Threads
1,214,431
Messages
6,119,457
Members
448,898
Latest member
drewmorgan128

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top