Speed up parent child hierarchy macro

sachin483

Board Regular
Joined
Mar 31, 2015
Messages
163
Office Version
  1. 2019
Platform
  1. Windows
i have macro for parent child hierarchy macro but i want to run the same for more than 20000 rows can it be speed up

VBA Code:
Option Explicit

Const SourceDivCol = 1
Const SourcePosCol = 2
Const SourceRepCol = 3
Const SourceLevCol = 4
Const SourceEmpCol = 5
Const SourceCodCol = 6
Const TargetDivCol = 15
Const TargetLevCol = 16
Const TargetPosCol = 17
Const TargetEmpCol = 18
Const TargetCodCol = 19
Dim SourceRow As Long
Dim TargetRow As Long

Sub CreateReport()
    Dim Boss As Range
    Dim Adr As String
    Dim Pos As String
    Application.ScreenUpdating = False
    TargetRow = 2
    Set Boss = Columns(SourceLevCol).Find(What:=1, LookAt:=xlWhole)
    Adr = Boss.Address
    Do
        SourceRow = Boss.Row
        TargetRow = TargetRow + 1
        Cells(TargetRow, TargetDivCol).Value = Cells(SourceRow, SourceDivCol).Value
        Cells(TargetRow, TargetLevCol).Value = Cells(SourceRow, SourceLevCol).Value
        Cells(TargetRow, TargetPosCol).Value = Cells(SourceRow, SourcePosCol).Value
        Cells(TargetRow, TargetEmpCol).Value = Cells(SourceRow, SourceEmpCol).Value
        Cells(TargetRow, TargetCodCol).Value = Cells(SourceRow, SourceCodCol).Value
        Pos = Cells(SourceRow, SourcePosCol).Value
        Call AddChildren(Pos)
        Set Boss = Columns(SourceLevCol).Find(What:=1, After:=Boss, LookAt:=xlWhole)
        If Boss Is Nothing Then Exit Do
    Loop Until Boss.Address = Adr
    Application.ScreenUpdating = True
End Sub

Sub AddChildren(BossPos As String)
    Dim Child As Range
    Dim Adr As String
    Dim Pos As String
    Set Child = Columns(SourceRepCol).Find(What:=BossPos, LookAt:=xlWhole)
    If Child Is Nothing Then Exit Sub
    Adr = Child.Address
    Do
        SourceRow = Child.Row
        TargetRow = TargetRow + 1
        Cells(TargetRow, TargetDivCol).Value = Cells(SourceRow, SourceDivCol).Value
        Cells(TargetRow, TargetLevCol).Value = Cells(SourceRow, SourceLevCol).Value
        Cells(TargetRow, TargetPosCol).Value = Cells(SourceRow, SourcePosCol).Value
        Cells(TargetRow, TargetEmpCol).Value = Cells(SourceRow, SourceEmpCol).Value
        Cells(TargetRow, TargetCodCol).Value = Cells(SourceRow, SourceCodCol).Value
        Pos = Cells(SourceRow, SourcePosCol).Value
        Call AddChildren(Pos)
        Set Child = Columns(SourceRepCol).Find(What:=BossPos, After:=Child, LookAt:=xlWhole)
        If Child Is Nothing Then Exit Do
    Loop Until Child.Address = Adr
End Sub
 

Excel Facts

Move date out one month or year
Use =EDATE(A2,1) for one month later. Use EDATE(A2,12) for one year later.
Hi @sachin483.

The process is slow because you are using methods like FIND that search the sheet directly, and also because you are updating directly on the sheet:

Rich (BB code):
Sub CreateReport()
...
    TargetRow = 2
    Set Boss = Columns(SourceLevCol).Find(What:=1, LookAt:=xlWhole)
    Do
        Cells(TargetRow, TargetDivCol).Value = Cells(SourceRow, SourceDivCol).Value
...
        Pos = Cells(SourceRow, SourcePosCol).Value
        Call AddChildren(Pos)
        Set Boss = Columns(SourceLevCol).Find(What:=1, After:=Boss, LookAt:=xlWhole)
...
End Sub

Sub AddChildren(BossPos As String)
...
    Set Child = Columns(SourceRepCol).Find(What:=BossPos, LookAt:=xlWhole)
...
        Cells(TargetRow, TargetDivCol).Value = Cells(SourceRow, SourceDivCol).Value
...
        Set Child = Columns(SourceRepCol).Find(What:=BossPos, After:=Child, LookAt:=xlWhole)
...
End Sub

To improve the process you should use other tools like dictionaries and arrays, which perform all operations in memory, the design should read only once from the sheet and write only once to the sheet. Let me explain, while your process performs many reads of the sheet and many writes to the sheet, with this type of technique you will only perform 1 read and 1 write.

But to help you and understand how your macro works, it is necessary that you provide us with a sample of your data and the result you want.
It would be great if you could share a workbook with the following: on sheet1 the original data and on sheet2 the result you want.
You could upload a copy of your file to a free site such www.dropbox.com or google drive. Once you do that, mark it for 'Sharing' and you will be given a link to the file that you can post here. If the workbook contains confidential information, you could replace it with generic data.

--------------
I hope to hear from you soon.
Respectfully
Dante Amor
--------------
 
Upvote 0
Try the following code.
In the sample file and some other tests I ran it works fine.
But you must try with the 20000 rows. You tell me how the test worked.
How long did your macro take and how long did my macro take.

Oh, and a little detail, according to your example, the starting position is in cell C2.

VBA Code:
Option Explicit

Dim j As Long
Dim dic1 As Object, dic2 As Object
Dim a As Variant, b As Variant, dta As Variant

Sub Level_Position_Module4()
  Dim i As Long, y As Long, fil As Long, col As Long
  
  Set dic1 = CreateObject("Scripting.Dictionary")
  Set dic2 = CreateObject("Scripting.Dictionary")
    
  a = Range("A2:F" & Range("C" & Rows.Count).End(xlUp).Row).Value2
  ReDim b(1 To UBound(a, 1) + 2, 1 To 5)              'output array
  ReDim dta(1 To UBound(a, 1), 1 To UBound(a, 1))     'array with rows and columns from array 'a'
  
  For i = 1 To UBound(a, 1)
    dic1(a(i, 2)) = a(i, 1) & "|" & a(i, 4) & "|" & a(i, 5) & "|" & a(i, 6)
    If Not dic2.exists(a(i, 3)) Then
      y = y + 1
      dta(y, 1) = i
      dic2(a(i, 3)) = y & "|" & 1
    Else
      fil = Split(dic2(a(i, 3)), "|")(0)
      col = Split(dic2(a(i, 3)), "|")(1)
      col = col + 1
      dta(fil, col) = i
      dic2(a(i, 3)) = fil & "|" & col
    End If
  Next
  
  Call recur(Range("C2").Value)                               'initial position
  Range("Q2").Resize(UBound(b, 1), UBound(b, 2)).Value = b    'Output
  j = 0
End Sub

Sub recur(pos)
  Dim num As Variant, i&, fil&, col&, nrow&
  
  If dic2.exists(pos) Then
    fil = Split(dic2(pos), "|")(0)
    col = Split(dic2(pos), "|")(1)
    For i = 1 To col
      nrow = dta(fil, i)
      num = a(nrow, 2)
      j = j + 1
      b(j, 1) = Split(dic1(num), "|")(0)
      b(j, 2) = Split(dic1(num), "|")(1)
      b(j, 3) = num
      b(j, 4) = Split(dic1(num), "|")(2)
      b(j, 5) = Split(dic1(num), "|")(3)
      Call recur(num)                     'recursion
    Next
  End If
End Sub

--------------
I hope to hear from you soon.
Respectfully
Dante Amor
--------------
 
Upvote 0
Tty your example from post #3.
The examples you test should have the same structure as your example.

If it has the same structure and it doesn't work then share your file for me to review.
 
Upvote 0
Sorry, it's my mistake, I assumed the first row in your original file shouldn't be there.
So your data starts at row 3 and the result too:
1682097970798.png


The change is minimal in the macro, I only changed the "2" to a "3".

I'll give you the modified code, try it and tell me.
VBA Code:
Dim j As Long
Dim dic1 As Object, dic2 As Object
Dim a As Variant, b As Variant, dta As Variant

Sub Level_Position_Module4()
  Dim i As Long, y As Long, fil As Long, col As Long
 
  Set dic1 = CreateObject("Scripting.Dictionary")
  Set dic2 = CreateObject("Scripting.Dictionary")
      'initial data start in row 3
  a = Range("A3:F" & Range("C" & Rows.Count).End(xlUp).Row).Value2
  ReDim b(1 To UBound(a, 1) + 2, 1 To 5)              'output array
  ReDim dta(1 To UBound(a, 1), 1 To UBound(a, 1))     'array with rows and columns from array 'a'
 
  For i = 1 To UBound(a, 1)
    dic1(a(i, 2)) = a(i, 1) & "|" & a(i, 4) & "|" & a(i, 5) & "|" & a(i, 6)
    If Not dic2.exists(a(i, 3)) Then
      y = y + 1
      dta(y, 1) = i
      dic2(a(i, 3)) = y & "|" & 1
    Else
      fil = Split(dic2(a(i, 3)), "|")(0)
      col = Split(dic2(a(i, 3)), "|")(1)
      col = col + 1
      dta(fil, col) = i
      dic2(a(i, 3)) = fil & "|" & col
    End If
  Next
 
  Call recur(a(1, 3))                                         'initial position
  Range("Q3").Resize(UBound(b, 1), UBound(b, 2)).Value = b    'Output
  j = 0
End Sub

Sub recur(pos)
  Dim num As Variant, i&, fil&, col&, nrow&
 
  If dic2.exists(pos) Then
    fil = Split(dic2(pos), "|")(0)
    col = Split(dic2(pos), "|")(1)
    For i = 1 To col
      nrow = dta(fil, i)
      num = a(nrow, 2)
      j = j + 1
      b(j, 1) = Split(dic1(num), "|")(0)
      b(j, 2) = Split(dic1(num), "|")(1)
      b(j, 3) = num
      b(j, 4) = Split(dic1(num), "|")(2)
      b(j, 5) = Split(dic1(num), "|")(3)
      Call recur(num)                     'recursion
    Next
  End If
End Sub

I also share the same file with the code and the result.
.
 
Upvote 0
I would like to add the example of this hierarchy made with recursion, since it is an important topic but very few solutions:

Input data:
Dante Amor
ABCDEF
1DATA
2DIVISIONPOSITIONPOSITION REPORTINGLEVEL_NOempnocode
3XXOZ00301INDOL1E1LL81
4XXLR0201OZ003012E2LL82
5XXLA0101LR02013E3LL83
6XXLA0201LR02013E4LL84
7XXLA0701LR02013E5LL85
8XXXX0502LA02014E6LL86
9XXXX0601LA02014E7LL87
10XXXX1901LA02014E8LL88
11XXXX2101LA02014E9LL89
12XXXX2201LA07014E10LL90
13XXXX0101LA01014E11LL91
14XXXX0102LA01014E12LL92
15XXXX0103LA01014E13LL93
16XXXX0104LA01014E14LL94
17XXXX0201LA07014E15LL95
18XXXX0301LA07014E16LL96
19XXXX0501LA02014E17LL97
Sheet1

Results:
Dante Amor
OPQRSTU
1Result of Macro
2DIVISIONLEVEL_NODIVISIONLEVEL_NOPOSITIONempnocode
3XX1XX1OZ00301E1LL81
4XX2XX2LR0201E2LL82
5XX3XX3LA0101E3LL83
6XX4XX4XX0101E11LL91
7XX4XX4XX0102E12LL92
8XX4XX4XX0103E13LL93
9XX4XX4XX0104E14LL94
10XX3XX3LA0201E4LL84
11XX4XX4XX0502E6LL86
12XX4XX4XX0601E7LL87
13XX4XX4XX1901E8LL88
14XX4XX4XX2101E9LL89
15XX4XX4XX0501E17LL97
16XX3XX3LA0701E5LL85
17XX4XX4XX2201E10LL90
18XX4XX4XX0201E15LL95
19XX4XX4XX0301E16LL96
Sheet1

I also take the opportunity to put the link to another similar situation of recursion:

I hope it helps you.
With affection Dante Amor.
 
Upvote 0
@DanteAmor

If the level no is sorted then only its work ie:- level 1 should be on top , if level no. is sorted from largest to small then it doesn't work
 
Upvote 0

Forum statistics

Threads
1,224,815
Messages
6,181,136
Members
453,021
Latest member
Justyna P

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