Skip to Content
Author's profile photo Amit Diwane

ABAP Recursive Code to Identify root nodes in Hierarchy fromTable Data

Dear ABAPers,

 

I have come across a scenario where I need to find out the root entry from a hierarchy. I would like to share the approach which I followed for the same. It might not be the best one but hope will help you in one or the other way 🙂

 

Consider the test data for our case:

 

Here First Column is the Child Node and Second Column is Parent Node.

Problem Statement: We need to identify the final child nodes for Parent ‘1’ and ‘2’.

For e.g: For parent 1 root nodes are A and B. And again for A the root node are C, D out of which D is final root node and for C we have again two child nodes M and N which are at end the root nodes.

Same way we need to identify all the root nodes.

 

ABAP Code For the same:

*&---------------------------------------------------------------------*
*& Report ZAM_RECURSIVE_TEST
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zam_recursive_test.
TYPES: BEGIN OF ty_data,      "To hold test data
         field1 TYPE char1,   "Child
         field2 TYPE char2,   "Parent
       END OF ty_data,

       BEGIN OF ty_out,      "To Filter
         field1 TYPE char1,
       END OF ty_out,

       BEGIN OF ty_sel,      "Range Table
         sign TYPE TVARV_SIGN,
         opti TYPE TVARV_OPTI,
         low  TYPE char32,
         high TYPE char32,
       END OF ty_sel.

TYPES tt_data TYPE TABLE OF ty_data WITH EMPTY KEY.

DATA: it_data TYPE TABLE OF ty_data,
      wa_data TYPE ty_data,
      it_rec  TYPE tt_data,
      wa_rec  TYPE ty_data,
      it_out  TYPE TABLE OF ty_out,
      it_sel  TYPE TABLE OF ty_sel,
      wa_sel  TYPE ty_sel.

"Create Test Data
wa_data-field1 = 'A'. wa_data-field2 = '1'.
APPEND wa_data TO it_data.
wa_data-field1 = 'B'. wa_data-field2 = '1'.
APPEND wa_data TO it_data.
wa_data-field1 = 'C'. wa_data-field2 = 'A'.
APPEND wa_data TO it_data.
wa_data-field1 = 'D'. wa_data-field2 = 'A'.
APPEND wa_data TO it_data.
wa_data-field1 = 'E'. wa_data-field2 = 'B'.
APPEND wa_data TO it_data.
wa_data-field1 = 'K'. wa_data-field2 = '2'.
APPEND wa_data TO it_data.
wa_data-field1 = 'L'. wa_data-field2 = 'K'.
APPEND wa_data TO it_data.
wa_data-field1 = 'M'. wa_data-field2 = 'C'.
APPEND wa_data TO it_data.
wa_data-field1 = 'N'. wa_data-field2 = 'C'.
APPEND wa_data TO it_data.
wa_data-field1 = 'O'. wa_data-field2 = 'E'.
APPEND wa_data TO it_data.
wa_data-field1 = 'P'. wa_data-field2 = 'B'.
APPEND wa_data TO it_data.
"Sort Test Data
SORT it_data by field2.
"Fetch Data for Parent 1
it_rec = VALUE tt_data( FOR wa_rec2 IN it_data WHERE ( field2 = '1' ) ( CORRESPONDING #( wa_rec2 ) )  ) .
"Call recursive code
PERFORM f_fetch TABLES it_rec.
"Fetch Data for Parent 2
it_rec = VALUE tt_data( FOR wa_rec2 IN it_data WHERE ( field2 = '2' ) ( CORRESPONDING #( wa_rec2 ) )  ) .
"Call recursive code
PERFORM f_fetch TABLES it_rec.

"Write Data
WRITE /: 'Original Data'.
WRITE /: 'Node'.
WRITE: '     Parent'.
LOOP AT it_data into wa_data.
"Loop on Original Data
  WRITE /: wa_data-field1.
  WRITE '       '.
  WRITE wa_data-field2.
ENDLOOP.

DATA ls_out TYPE ty_out.
WRITE /: '----------------------------------------------'.
WRITE /: 'Children Nodes Identified'.
LOOP AT it_out INTO ls_out.
"Loop for Children Node
WRITE /: ls_out-field1.
wa_sel-sign = 'I'.
wa_sel-opti = 'EQ'.
wa_sel-low = ls_out-field1.
APPEND wa_sel TO it_sel.
ENDLOOP.

WRITE /: '----------------------------------------------'.
WRITE /: 'Complete Details for Children Nodes'.
WRITE /: 'Node'.
WRITE: '     Parent'.

LOOP AT it_data into wa_data where field1 IN it_sel.
"Loop on Children complete data
  WRITE /:  wa_data-field1.
  WRITE '       '.
  WRITE wa_data-field2.
ENDLOOP.


FORM f_fetch TABLES pt_rec TYPE tt_data.
  DATA: ls_rec   TYPE ty_data,
        wa_data2 TYPE ty_data,
        ls_out   TYPE ty_out,
        lv_lines TYPE i.
  LOOP AT pt_rec INTO ls_rec.
    READ TABLE it_data INTO wa_data2 WITH KEY field2 = ls_rec-field1 BINARY SEARCH.
    IF sy-subrc IS INITIAL.
      DATA(it_pass) = VALUE tt_data( FOR wa_rec2 IN it_data WHERE ( field2 = ls_rec-field1 ) ( CORRESPONDING #( wa_rec2 ) )  ) .
      DESCRIBE TABLE it_pass LINES lv_lines.
      IF lv_lines GE 1.
        PERFORM f_fetch TABLES it_pass.
      ELSE.
        CLEAR: ls_rec,it_pass.
        ls_rec-field1 = wa_data2-field1.
        APPEND ls_rec TO it_pass.
        PERFORM f_fetch TABLES it_pass.
      ENDIF.

    ELSE.
      ls_out-field1 = ls_rec-field1.
      APPEND ls_out TO it_out.
    ENDIF.
  ENDLOOP.

ENDFORM.

 

Output :

 

Happy Learning 🙂

Assigned Tags

      2 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali

      Happy Learning ?

      Thanks!

      I think you are using a depth first traversal methods of a tree.

      Since your code is using operators, I though of massively using them and came up with this version:

      *&---------------------------------------------------------------------*
      *& Report ZAM_RECURSIVE_TEST
      *&---------------------------------------------------------------------*
      *& https://blogs.sap.com/2018/07/06/abap-recursive-code-to-identify-root-nodes-in-hierarchy-fromtable-data/
      *&---------------------------------------------------------------------*
      REPORT zam_recursive_test.
      TYPES: BEGIN OF ty_data,      "To hold test data
               field1 TYPE char1,   "Child
               field2 TYPE char2,   "Parent
             END OF ty_data,
             tt_tree TYPE SORTED TABLE OF ty_data WITH UNIQUE KEY field1 field2,
      
             BEGIN OF ty_out,      "To Filter
               field1 TYPE char1,
             END OF ty_out,
             tt_out TYPE STANDARD TABLE OF ty_out.
      
      START-OF-SELECTION.
        PERFORM children.
      
      FORM children.
        DATA lt_tree TYPE tt_tree.
        DATA lt_list  TYPE tt_out.
      
        PERFORM generate_tree CHANGING lt_tree.
        PERFORM search_tree USING lt_tree '1' CHANGING lt_list.
        PERFORM search_tree USING lt_tree '2' CHANGING lt_list.
        PERFORM list_view USING lt_tree lt_list.
      ENDFORM.
      
      FORM data_for_parent USING pt_tree TYPE tt_tree
                                 pv_root TYPE any
                           CHANGING ct_tree TYPE tt_tree.
        ct_tree = VALUE #( FOR wa_tree IN pt_tree WHERE ( field2 = pv_root ) ( wa_tree )  ) .
      ENDFORM.
      
      FORM search_tree USING pt_tree TYPE tt_tree
                             pv_root TYPE ty_data-field2
                       CHANGING ct_out TYPE tt_out.
        DATA lt_rec TYPE tt_tree.
        PERFORM data_for_parent USING pt_tree pv_root CHANGING lt_rec.
        PERFORM f_fetch USING pt_tree CHANGING lt_rec ct_out.
      ENDFORM.
      
      FORM list_view USING pt_tree TYPE tt_tree
                           pt_list TYPE tt_out.
        DATA lt_filter TYPE SORTED TABLE OF char1 WITH UNIQUE KEY table_line.
      
        PERFORM display_nodes USING pt_tree 'Original Data' .
        
        WRITE /: '----------------------------------------------'.
        WRITE /: 'Children Nodes Identified'.
        LOOP AT pt_list INTO DATA(ls_out).
          "Loop for Children Node
          WRITE /: ls_out-field1.
          INSERT ls_out-field1 INTO TABLE lt_filter.
        ENDLOOP.
      
        DATA(lt_children) = FILTER tt_tree( pt_tree IN lt_filter WHERE field1 EQ table_line ).
      
        WRITE /: '----------------------------------------------'.
        PERFORM display_nodes USING lt_children 'Complete Details for Children Nodes' .
      ENDFORM.
      
      FORM display_nodes USING pt_tree TYPE tt_tree
                               pv_title TYPE string.
        WRITE /: pv_title.
        WRITE /: 'Node     Parent'.
        LOOP AT pt_tree INTO DATA(wa_data).
          WRITE /: |{ wa_data-field1 }       { wa_data-field2 }|.
        ENDLOOP.
      ENDFORM.
      
      FORM f_fetch USING pt_tree TYPE tt_tree
                   CHANGING ct_rec TYPE tt_tree
                            ct_list TYPE tt_out.
        DATA lt_parents TYPE tt_tree.
        LOOP AT ct_rec INTO DATA(ls_rec).
          ASSIGN pt_tree[ field2 = ls_rec-field1 ] TO FIELD-SYMBOL(<ls_tree>).
          IF sy-subrc IS INITIAL.
            PERFORM data_for_parent USING pt_tree ls_rec-field1 CHANGING lt_parents.
            IF lines( lt_parents ) EQ 0.
              lt_parents = VALUE #( ( field1 = <ls_tree>-field1 ) ).
            ENDIF.
            PERFORM f_fetch USING pt_tree CHANGING lt_parents ct_list.
          ELSE.
            APPEND VALUE #( field1 = ls_rec-field1 ) TO ct_list.
          ENDIF.
        ENDLOOP.
      ENDFORM.
      
      FORM generate_tree CHANGING ct_data TYPE tt_tree.
      * Create Test Data
        ct_data = VALUE #( ( field1 = 'A' field2 = '1' )
                           ( field1 = 'B' field2 = '1' )
                           ( field1 = 'C' field2 = 'A' )
                           ( field1 = 'D' field2 = 'A' )
                           ( field1 = 'E' field2 = 'B' )
                           ( field1 = 'K' field2 = '2' )
                           ( field1 = 'L' field2 = 'K' )
                           ( field1 = 'M' field2 = 'C' )
                           ( field1 = 'N' field2 = 'C' )
                           ( field1 = 'O' field2 = 'E' )
                           ( field1 = 'P' field2 = 'B' ) ).
      ENDFORM.
      Author's profile photo ajay mukundan
      ajay mukundan

      Thanks for this program!