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..
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!
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
Thanks,
Naimesh Patel
Hi Naimesh,
Thanks a lot for the example.!!!
regards
Subham