Create dynamic parent-child hierarchy?

680 views Asked by At

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.

1

There are 1 answers

0
Florian On BEST ANSWER

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.