Skip to Content

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 🙂

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

  1. Jacques Nomssi

    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.
    (1) 

Leave a Reply