ABAP Transpose Internal Table

By | November 12, 2013 | Utilities | 24,896 | 4

Many times you need to transpose the data from row based to column based and vice versa. This simple utility classes would help to do exactly the same.

Preface

Recently, I had to develop a report for which I need to display Condition Types for give sales orders. Since user was interested in only few of the Condition Types, they wanted to see the Condition Types as Columns. So they can see multiple order’s conditions and use the ALV’s functionalities like Total, subtotal for those condition values.

Something like this:

The selected condition data from the tables are like the conditions stored in the KONV – Single record for each condition for a document. I wrote a logic to transpose that data from row based to column based. Later, I developed these classes to transpose the data from Row Based to column based and vice versa.

Involved Components

These the are various classes and interface for this utility:

  • ZIF_TRANSPOSER – Interface containing common method structures
  • ZCL_TRANSPOSE_TO_COLUMNS – Concrete class implementing the ZIF_TRANSPOSER to transpose the data from Row Based to Columns.
  • ZCL_TRANSPOSE_TO_ROWS – Concrete Class implementing the ZIF_TRANSPOSER to transpose the data from Column Based to Row based. Exactly opposite activity from the class ZCL_TRANSPOSE_TO_COLUMNS.

UML Diagram

On UML relationship

Code Lines

Code lines for the Utility

Utility Class to Transpose Internal Table

 
*&---------------------------------------------------------------------*
*& Purpose  : Utility to transpose Internal Table - 
*&            Row to Column and Column to Row
*& Author   : Naimesh Patel
*& URL      : http://zevolving.com/?p=2463
*&---------------------------------------------------------------------*
*
INTERFACE zif_transposer.
  TYPES:
    BEGIN OF ty_rows,
      row     TYPE i,
      field   TYPE char30,
      dename  TYPE char30,
      value   TYPE string,
    END   OF ty_rows.
  TYPES: tt_rows TYPE STANDARD TABLE OF ty_rows.
 
  TYPES:
    BEGIN OF ty_fields,
      field TYPE char30,
      dename TYPE char30,
    END   OF ty_fields.
  TYPES: tt_fields TYPE STANDARD TABLE OF ty_fields.
 
  METHODS:
    transpose
        RETURNING value(ro_data) TYPE REF TO data.
 
  DATA: t_fields TYPE tt_fields.
  DATA: t_data   TYPE tt_rows.
  DATA: o_dyna_table TYPE REF TO data.
  DATA: o_dyna_wa    TYPE REF TO data.
 
  METHODS: create_dynamic_table.
  METHODS: collect_fields.
 
ENDINTERFACE.                    "zif_transposer
*
CLASS zcl_transpose_to_columns DEFINITION.
  PUBLIC SECTION.
    INTERFACES: zif_transposer.
    METHODS:
      constructor
        IMPORTING
          it_data   TYPE zif_transposer=>tt_rows
          it_fields TYPE zif_transposer=>tt_fields OPTIONAL.
 
  PRIVATE SECTION.
    ALIASES: t_data   FOR zif_transposer~t_data.
    ALIASES: t_fields FOR zif_transposer~t_fields.
    ALIASES: o_dyna_table FOR zif_transposer~o_dyna_table.
    ALIASES: o_dyna_wa    FOR zif_transposer~o_dyna_wa.
 
 
ENDCLASS.                    "zcl_transpose_to_columns DEFINITION
*
CLASS zcl_transpose_to_rows DEFINITION.
  PUBLIC SECTION.
    INTERFACES: zif_transposer.
    METHODS:
      constructor
        IMPORTING
          it_data   TYPE ANY TABLE
          it_fields TYPE zif_transposer=>tt_fields OPTIONAL.
 
  PRIVATE SECTION.
    ALIASES: t_data   FOR zif_transposer~t_data.
    ALIASES: t_fields FOR zif_transposer~t_fields.
    ALIASES: o_dyna_table FOR zif_transposer~o_dyna_table.
    ALIASES: o_dyna_wa    FOR zif_transposer~o_dyna_wa.
ENDCLASS.                    "zcl_transpose_to_rows DEFINITION
*
CLASS zcl_transpose_to_columns IMPLEMENTATION.
  METHOD constructor.
    t_data   = it_data.
    t_fields = it_fields.
 
  ENDMETHOD.                    "constructor
  METHOD zif_transposer~transpose.
 
    FIELD-SYMBOLS:
      <f_tab> TYPE STANDARD TABLE,
      <f_wa>  TYPE any,
      <f_field> TYPE any.
    DATA: ls_data LIKE LINE OF me->t_data.
 
*   create dynamic table
    me->zif_transposer~collect_fields( ).
    me->zif_transposer~create_dynamic_table( ).
 
*   Move data to Dynamica table
    ASSIGN o_dyna_table->*  TO <f_tab>.
    ASSIGN o_dyna_wa->*     TO <f_wa>.
 
    LOOP AT me->t_data INTO ls_data.
      ASSIGN COMPONENT ls_data-field OF STRUCTURE <f_wa> TO <f_field>.
      IF sy-subrc EQ 0.
        <f_field> = ls_data-value.
      ENDIF.
      AT END OF row.
        APPEND <f_wa> TO <f_tab>.
        CLEAR  <f_wa>.
      ENDAT.
    ENDLOOP.
 
*   send data back
    ro_data = o_dyna_table.
 
  ENDMETHOD.                    "zif_transposer~transpose
 
  METHOD zif_transposer~create_dynamic_table.
 
    DATA: lo_struct   TYPE REF TO cl_abap_structdescr,
          lo_element  TYPE REF TO cl_abap_elemdescr,
          lo_new_type TYPE REF TO cl_abap_structdescr,
          lo_new_tab  TYPE REF TO cl_abap_tabledescr,
          lt_comp     TYPE cl_abap_structdescr=>component_table,
          lt_tot_comp TYPE cl_abap_structdescr=>component_table,
          la_comp     LIKE LINE OF lt_comp.
 
    DATA: ls_field    LIKE LINE OF me->t_fields.
    DATA: lv_name     TYPE string.
 
    LOOP AT me->t_fields INTO ls_field.
      IF ls_field-dename IS INITIAL.
        lv_name = ls_field-field.
      ELSE.
        lv_name = ls_field-dename.
      ENDIF.
 
*   Element Description
      lo_element ?= cl_abap_elemdescr=>describe_by_name( lv_name ).
 
*   Components
      la_comp-name = ls_field-field.
      la_comp-type = lo_element.
      APPEND la_comp TO lt_tot_comp.
      CLEAR: la_comp.
    ENDLOOP.
 
*   Create a New Type
    lo_new_type = cl_abap_structdescr=>create( lt_tot_comp ).
 
*   New Table type
    lo_new_tab = cl_abap_tabledescr=>create(
                    p_line_type  = lo_new_type
                    p_table_kind = cl_abap_tabledescr=>tablekind_std
                    p_unique     = abap_false ).
 
*   data to handle the new table type
    CREATE DATA o_dyna_table TYPE HANDLE lo_new_tab.
    CREATE DATA o_dyna_wa    TYPE HANDLE lo_new_type.
 
  ENDMETHOD.                    "create_dynamic_table
  METHOD zif_transposer~collect_fields.
 
    CHECK t_fields IS INITIAL.
 
    DATA: lv_first_field  TYPE char30.
    DATA: ls_data         LIKE LINE OF me->t_data.
    DATA: ls_field        LIKE LINE OF me->t_fields.
    LOOP AT me->t_data INTO ls_data.
      IF lv_first_field = ls_data-field.
        EXIT.
      ENDIF.
      IF lv_first_field IS INITIAL.
        lv_first_field = ls_data-field.
      ENDIF.
      MOVE-CORRESPONDING ls_data TO ls_field.
      APPEND ls_field TO me->t_fields.
    ENDLOOP.
 
  ENDMETHOD.                    "collect_Fields
ENDCLASS.                    "zcl_transpose_to_columns IMPLEMENTATION
 
*
CLASS zcl_transpose_to_rows IMPLEMENTATION.
  METHOD constructor.
    GET REFERENCE OF it_data INTO o_dyna_table.
    t_fields = it_fields.
  ENDMETHOD.                    "constructor
  METHOD zif_transposer~transpose.
    FIELD-SYMBOLS:
      <f_tab> TYPE STANDARD TABLE,
      <f_wa>  TYPE any,
      <f_field> TYPE any.
 
    DATA: lt_rows TYPE zif_transposer=>tt_rows.
    DATA: ls_rows LIKE LINE OF lt_rows,
          lv_row  TYPE i.
    DATA: ls_fields LIKE LINE OF t_fields.
 
    me->zif_transposer~collect_fields( ).
    me->zif_transposer~create_dynamic_table( ).
 
    ASSIGN o_dyna_table->* TO <f_tab>.
    LOOP AT <f_tab> ASSIGNING <f_wa>.
      lv_row = sy-tabix.
      LOOP AT t_fields INTO ls_fields.
        ASSIGN COMPONENT ls_fields-field OF STRUCTURE <f_wa> TO <f_field>.
        IF sy-subrc EQ 0.
          ls_rows-row   = lv_row.
          ls_rows-field = ls_fields-field.
          ls_rows-dename = ls_fields-dename.
          ls_rows-value  = <f_field>.
          APPEND ls_rows TO lt_rows.
        ENDIF.
      ENDLOOP.
    ENDLOOP.
 
    me->t_data = lt_rows.
    GET REFERENCE OF me->t_data INTO ro_data.
 
  ENDMETHOD.                    "zif_transposer~transpose
  METHOD zif_transposer~create_dynamic_table.
 
*   Setting up the o_dyna_wa is unnecessary, just sake of completeness
*   of this method
    FIELD-SYMBOLS:
      <f_tab> TYPE STANDARD TABLE,
      <f_wa>  TYPE any.
 
    ASSIGN o_dyna_table->* TO <f_tab>.
    READ TABLE <f_tab> ASSIGNING <f_wa> INDEX 1.
    IF sy-subrc EQ 0.
      GET REFERENCE OF <f_wa> INTO o_dyna_wa.
    ENDIF.
 
  ENDMETHOD.                    "zif_transposer~create_dynamic_table
  METHOD zif_transposer~collect_fields.
 
    DATA: lo_tab      TYPE REF TO cl_abap_tabledescr,
          lo_struc    TYPE REF TO cl_abap_structdescr,
          lt_comp     TYPE cl_abap_structdescr=>component_table,
          ls_comp     LIKE LINE OF lt_comp,
          ls_fields   LIKE LINE OF t_fields.
 
    IF t_fields IS INITIAL.
      lo_tab ?= cl_abap_tabledescr=>describe_by_data_ref( o_dyna_table ).
      lo_struc ?= lo_tab->get_table_line_type( ).
      lt_comp = lo_struc->get_components( ).
      LOOP AT lt_comp INTO ls_comp.
        ls_fields-field = ls_comp-name.
        ls_fields-dename = ls_comp-type->get_relative_name( ).
        APPEND ls_fields TO t_fields.
      ENDLOOP.
    ENDIF.
 
  ENDMETHOD.                    "zif_transposer~collect_fields
ENDCLASS.                    "zcl_transpose_to_rows IMPLEMENTATION
 

Demo Program
Here is the simple demo program to test the utility class

 
START-OF-SELECTION.
* test data
  DATA: lt_rows TYPE zif_transposer=>tt_rows.
  DATA: ls_rows LIKE LINE OF lt_rows.
  ls_rows-row   = 1.
  ls_rows-field = 'MATNR'.
  ls_rows-value = '1-234-123'.
  APPEND ls_rows TO lt_rows.    CLEAR  ls_rows.
  ls_rows-row   = 1.
  ls_rows-field = 'MAKTX'.
  ls_rows-value = 'Test Material 1'.
  APPEND ls_rows TO lt_rows.    CLEAR  ls_rows.
  ls_rows-row   = 1.
  ls_rows-field = 'AMOUNT'.
  ls_rows-dename = 'DMBTR'.
  ls_rows-value = '123.45'.
  APPEND ls_rows TO lt_rows.    CLEAR  ls_rows.
  ls_rows-row   = 2.
  ls_rows-field = 'MATNR'.
  ls_rows-value = '9-123-123'.
  APPEND ls_rows TO lt_rows.    CLEAR  ls_rows.
  ls_rows-row   = 2.
  ls_rows-field = 'MAKTX'.
  ls_rows-value = 'Assembly 123'.
  APPEND ls_rows TO lt_rows.    CLEAR  ls_rows.
 
* Transpose the data to columns
  DATA: lo_col_transposer TYPE REF TO zif_transposer.
  DATA: lo_col_data   TYPE REF TO data.
  FIELD-SYMBOLS:<f_output> TYPE STANDARD TABLE.
  CREATE OBJECT lo_col_transposer
    TYPE
    zcl_transpose_to_columns
    EXPORTING
      it_data = lt_rows.
  lo_col_data = lo_col_transposer->transpose( ).
  ASSIGN lo_col_data->* TO <f_output>.
 
  DATA: o_salv TYPE REF TO cl_salv_table.
  TRY.
      CALL METHOD cl_salv_table=>factory
        IMPORTING
          r_salv_table = o_salv
        CHANGING
          t_table      = <f_output>.
      o_salv->display( ).
    CATCH cx_salv_msg .
  ENDTRY.
 
 
* Transpose to rows
  DATA: lo_row_transposer TYPE REF TO zif_transposer.
  DATA: lo_row_data TYPE REF TO data.
  FIELD-SYMBOLS: <lfs_row_tab> TYPE zif_transposer=>tt_rows.
  CREATE OBJECT lo_row_transposer
    TYPE
    zcl_transpose_to_rows
    EXPORTING
      it_data = <f_output>.
  lo_row_data = lo_row_transposer->transpose( ).
  ASSIGN lo_row_data->* TO <lfs_row_tab>.
 
  DATA: o_salv2 TYPE REF TO cl_salv_table.
  TRY.
      CALL METHOD cl_salv_table=>factory
        IMPORTING
          r_salv_table = o_salv2
        CHANGING
          t_table      = <lfs_row_tab>.
      o_salv2->display( ).
    CATCH cx_salv_msg .
  ENDTRY.
 

Flexibility

Both classes can excepts a Fields Table. You can use this to only get desired fields and its values back from the source. E.g. When transposing from Rows to Columns, you only want certain fields to be provided back in the return table, you can pass only those fields in this Field Table.

Output

After Rows to Columns

After Columns to Rows, which is same as what we have started with the test data..

Like It? Share!!

Don't miss an Update

Get notified of the new post, right into your inbox

Naimesh Patel{274 articles}

I'm SAP ABAP Consultant for more than a decade. I like to experiment with ABAP especially OO. I have been SDN Top Contributor.
Follow :

Explore all of his 274 articles.

Load comments

4 Comments

  • Steve Oldner

    Haven’t had the need to do this so this is new for me. Very neat! And I really like the UML that you add.

    Thanks for the lesson!

  • Subham Sarkar

    Hi Naimesh,

    Need to know if the following scenario possible, if I have a sales order internal table with 1000 rows of sales order info, and i need to copy only one column, i.e. VBELN to another internal table which will hold only one column i.e. VBELN, 1000 sales order no, how to achieve this.

    One way is to loop at the first table and then fill the second table with only sales order no.

    Do you have any other way, avoiding loop?

    thanks for your reply.

    regards
    Subham

  • Hello Subham,

    You can use the APPEND LINES OF to copy the data from one table to another table without loop. But, note that this will do the LOOP Internally to do the copy from table1 to table2.

    Like this:

    Zevolving ABAP Syntax Highlighter

     
    TYPES:
      BEGIN OF ty_vbak,
        vbeln TYPE vbak-vbeln,
        auart TYPE vbak-auart,
        vkorg TYPE vbak-vkorg,
      END   OF ty_vbak.
    DATA: t_vbak TYPE STANDARD TABLE OF ty_vbak.
    DATA: t_vbeln TYPE STANDARD TABLE OF vbak-vbeln.
     
    SELECT vbeln auart vkorg
      FROM vbak
      INTO TABLE t_vbak
      UP TO 500 ROWS.
     
    APPEND LINES OF t_vbak TO t_vbeln.
     
    WRITE 'done'.
     

    Thanks,
    Naimesh Patel

  • Subham Sarkar

    Hi Naimesh,

    Thanks a lot for the example.!!!

    regards
    Subham

Comments on this Post are now closed. If you have something important to share, you can always contact me.

You seem to be new here. Subscribe to stay connected.