Search code examples
parent-childabaphierarchyinternal-tables

Create dynamic parent-child hierarchy?


do you know a efficient method to generate a parent-child hierarchy from a table without parent node IDs. The parent child relationship should be switchable. All of this should be done in ABAP.

example data:

Color   producer    weight  Airplane(key)
green   CompanyA    330     A350
green   CompanyA    222     A320
green   CompanyB    222     B450
yellow  CompanyA    330     H450

I want generate a child parent relationship based on this rows: producer weight Airplane and ignore color. Then i will change it and use: Color weight Airplane and ignore producer

At the end i need a result looks like for "producer weight Airplane"

       CompanyA               CompanyB
    330       222         222
A350             A320     B450
H450             

in a internal table it should look like this at the end

ID  attribute   value       H_level parentID
1   producer    CompanyA    1       
2   weight      330         2       1
3   airplane    A350        3       2
.....

does anyone have a good efficient idea? Thanks a lot.


Solution

  • Here is a simple tree-building algorithm that does what you need.

    Note that this algorithm is not optimized in terms of performance. If you manage large amounts of data, you may want to revise some aspects, for example use SORTED or HASHED rather than STANDARD tables to improve the lookup READ TABLEs.

    The algorithm is also not refactored for optimum code style. For example, clean code suggests we may want to extract a couple of methods to improve readability.

    CLASS hierarchy_builder DEFINITION
        PUBLIC FINAL CREATE PUBLIC.
    
      PUBLIC SECTION.
    
        TYPES:
          BEGIN OF ts_input,
            color    TYPE string,
            producer TYPE string,
            weight   TYPE string,
            airplane TYPE string,
          END OF ts_input.
    
        TYPES tt_input TYPE
          STANDARD TABLE OF ts_input
            WITH EMPTY KEY.
    
        TYPES tt_attributes TYPE string_table.
    
        TYPES:
          BEGIN OF ts_output,
            id        TYPE string,
            attribute TYPE string,
            value     TYPE string,
            level     TYPE i,
            parent_id TYPE string,
          END OF ts_output.
    
        TYPES tt_output
          TYPE STANDARD TABLE OF ts_output
            WITH EMPTY KEY.
    
        CLASS-METHODS build_hierarchy
          IMPORTING
            it_data          TYPE tt_input
            it_hierarchy     TYPE tt_attributes
          RETURNING
            VALUE(rt_result) TYPE tt_output.
    
    ENDCLASS.
    
    CLASS hierarchy_builder IMPLEMENTATION.
    
      METHOD build_hierarchy.
    
        DATA(lv_parent_attribute) = ``.
        DATA(lv_next_id) = 1.
    
        LOOP AT it_hierarchy INTO DATA(lv_child_attribute).
    
          DATA(lv_level) = sy-tabix.
    
          LOOP AT it_data INTO DATA(ls_data).
    
            DATA(lv_parent_id) = ``.
    
            ASSIGN COMPONENT lv_child_attribute
              OF STRUCTURE ls_data
              TO FIELD-SYMBOL(<lv_child_value>).
    
            IF lv_parent_attribute IS NOT INITIAL.
    
              ASSIGN COMPONENT lv_parent_attribute
                OF STRUCTURE ls_data
                TO FIELD-SYMBOL(<lv_parent_value>).
    
              READ TABLE rt_result
                INTO DATA(ls_parent)
                WITH KEY
                  attribute = lv_parent_attribute
                  value = <lv_parent_value>.
    
              lv_parent_id = ls_parent-id.
    
            ENDIF.
    
            READ TABLE rt_result
              TRANSPORTING NO FIELDS
              WITH KEY
                attribute = lv_child_attribute
                value = <lv_child_value>
                parent_id = lv_parent_id.
    
            IF sy-subrc <> 0.
    
              INSERT VALUE #(
                  id = |{ lv_next_id }|
                  attribute = lv_child_attribute
                  value = <lv_child_value>
                  level = lv_level
                  parent_id = lv_parent_id )
                INTO TABLE rt_result.
    
              lv_next_id += 1.
    
            ENDIF.
    
          ENDLOOP.
    
          lv_parent_attribute = lv_child_attribute.
    
        ENDLOOP.
    
      ENDMETHOD.
    
    ENDCLASS.
    

    Verified with the following unit test. It represents the example you gave:

    CLASS ltc_unit_tests DEFINITION
        FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
    
      PRIVATE SECTION.
    
        METHODS builds_example FOR TESTING.
    
    ENDCLASS.
    
    CLASS ltc_unit_tests IMPLEMENTATION.
    
      METHOD builds_example.
    
        DATA(lt_data) =
          VALUE hierarchy_builder=>tt_input(
            ( color = 'green' producer = 'CompanyA' weight = '330' airplane = 'A350' )
            ( color = 'green' producer = 'CompanyA' weight = '222' airplane = 'A320' )
            ( color = 'green' producer = 'CompanyB' weight = '222' airplane = 'B450' )
            ( color = 'yellow' producer = 'CompanyA' weight = '330' airplane = 'H450' ) ).
    
        DATA(lt_hierarchy) =
          VALUE hierarchy_builder=>tt_attributes(
            ( `PRODUCER` )
            ( `WEIGHT` )
            ( `AIRPLANE` ) ).
    
        DATA(lt_result) =
          hierarchy_builder=>build_hierarchy(
            it_data = lt_data
            it_hierarchy = lt_hierarchy ).
    
        DATA(lt_expected) =
          VALUE hierarchy_builder=>tt_output(
            ( id = '1' attribute = 'PRODUCER' value = 'CompanyA' level = 1 parent_id = '' )
            ( id = '2' attribute = 'PRODUCER' value = 'CompanyB' level = 1 parent_id = '' )
            ( id = '3' attribute = 'WEIGHT' value = '330' level = 2 parent_id = '1' )
            ( id = '4' attribute = 'WEIGHT' value = '222' level = 2 parent_id = '1' )
            ( id = '5' attribute = 'WEIGHT' value = '222' level = 2 parent_id = '2' )
            ( id = '6' attribute = 'AIRPLANE' value = 'A350' level = 3 parent_id = '3' )
            ( id = '7' attribute = 'AIRPLANE' value = 'A320' level = 3 parent_id = '4' )
            ( id = '8' attribute = 'AIRPLANE' value = 'B450' level = 3 parent_id = '4' )
            ( id = '9' attribute = 'AIRPLANE' value = 'H450' level = 3 parent_id = '3' ) ).
    
        cl_abap_unit_assert=>assert_equals(
            act = lt_result
            exp = lt_expected ).
    
      ENDMETHOD.
    
    ENDCLASS.