Skip to Content
Personal Insights
Author's profile photo Akhilesh Vatupalli

ABAP Trivia: Transpose a 2D Array type Internal Table

Hello Everyone,

This is my first blog post in SAP community and I hope this post gives a trivial yet powerful capability of ABAP as a programming language.

From developer perspective ABAP gives most of the in built features like Sorting, Binary Search etc.,

In this blog post I tried to implement most commonly used open source concepts like transposing 2 dimensional array using an internal table using ABAP. I have used ABAP 7.50 syntaxes (a must try from your side as well!) in the logic to shrink the lines of code.

Without wasting time let’s get into the topics.

Transposing a 2D array using Internal table:

Transposing simply means “cause (two or more things) to exchange places”. In our example Row values become columns and vice versa.

In open source world a 2D Array looks like below,

[[1, 1, 1],
[2, 2, 2],
[3, 3, 3]]

and the transposed form looks like below,

[[1, 2, 3],
[1, 2, 3],
[1, 2, 3]]

Same thing cannot be represented in ABAP because we have only internal tables as data structures. In ABAP world the same above Array becomes an internal table with 3 columns and 3 rows.

lt_2d_array VALUE #f1 f2 f3 )
f1 f2 f3 )
f1 f2 f3 ) ).

Looking similar to above Array? Now let’s try to write a small piece of code to get the “Transpose” effect. We need to exchange the values from each column of each row to particular column of individual rows.

For example f1 value of row 1 doesn’t need to be exchanged, but f2 value of row 1 should be exchanged with f1 value of row 2. That is the base for our logic.

As we have fixed number of columns I have hard coded the number columns.

TYPES: BEGIN OF ty_2d_array,
         f1 TYPE i,
         f2 TYPE i,
         f3 TYPE i,
       END OF ty_2d_array.
DATA:
  lt_2d_array   TYPE STANDARD TABLE OF ty_2d_array WITH DEFAULT KEY.

* Prepopulate the values.
lt_2d_array = VALUE #( ( f1 = 1 f2 = 1 f3 = 1 )
                       ( f1 = 2 f2 = 2 f3 = 2 )
                       ( f1 = 3 f2 = 3 f3 = 3 ) ).
DATA(lt_2d_array_before) = lt_2d_array.

LOOP AT lt_2d_array ASSIGNING FIELD-SYMBOL(<fs_2d_array_in>).
  DATA(lv_tabix) = sy-tabix.

  DO 3 TIMES. " Hard coded column count
    IF lv_tabix < sy-index.
      ASSIGN COMPONENT sy-index OF STRUCTURE <fs_2d_array_in> TO FIELD-SYMBOL(<fv_swap_src>).

      " Swap the column of this index with next record.
      READ TABLE lt_2d_array ASSIGNING FIELD-SYMBOL(<fs_next_row>) INDEX sy-index.
      IF sy-subrc = 0.
        ASSIGN COMPONENT lv_tabix OF STRUCTURE <fs_next_row> TO FIELD-SYMBOL(<fv_swap_tar>).
        DATA(tmp)     = CONV i( <fv_swap_src> ).
        <fv_swap_src> = <fv_swap_tar>.
        <fv_swap_tar> = tmp.
      ENDIF.
    ENDIF.
  ENDDO.
ENDLOOP.

* Print to the demo output.
cl_demo_output=>write( data = lt_2d_array_before name = |Before Transpose| ).
cl_demo_output=>display( data = lt_2d_array name = |After Transpose| ).

Now let’s see the output,

See how our ABAP output is looking similar to normal JS Array! Looks nice? Yesss!

Hope you enjoyed this post! Thank you everyone for taking time to read the content!

 

Enjoy Coding,

Akhilesh

Assigned Tags

      4 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Matthew Billingham
      Matthew Billingham

      You could implement arrays with instances of a class comprising an array element, with references to one another. Something like...

      INTERFACE if_array_element.
        METHODS:
          get_next_one_along
            RETURNING_VALUE(r_Result) type ref to if_array_element,
          get_previous_one_along
            RETURNING_VALUE(r_Result) type ref to if_array_element,
          get_element_below
            RETURNING_VALUE(r_Result) type ref to if_array_element,
          get_element_above
            RETURNING_VALUE(r_Result) type ref to if_array_element.
      ENDINTERFACE. 
      
      CLASS array_element DEFINITION.
        PUBLIC SECTION.
          INTERFACES if_array_element.
        PRIVATE SECTION.
          DATA:
            next type ref to if_array_element,
            prev type ref to if_array_element,
            above type ref to if_array_element,
            below type ref to if_array_element,
            value type ref to object.
      ENDCLASS.
      

      How would you implement a transpose with such an object?

      Author's profile photo Frederik Hudak
      Frederik Hudak

      Sometimes, the winning move is not to play.

      If you structure your array like this, swapping two elements requires updating up to 16 pointers!

      Worst case - swapped are in the middle: 2 updated nodes  * (1 neighbor pointer for all 4 neighbors of the swapped node + all 4 neighbor pointers for the swapped node)

      A better approach is for a node to know its position x, y and a have reference to the array, then the neighbors can easily be computed in the getters

      prev = arr[x-1][y]
      
      next  = arr[x+1][y]
      
      above = arr[x][y-1]
      
      below = arr[x][y+1]
      

      And since you're only holding the value and not the element's position, to find out where you are, you'd have to walk left/up while counting until you hit a wall. Ouch!

      But we can go lazier...

      METHODS lazy_transpose
        IMPORTING x TYPE i
                  y TYPE i
        RETURNING VALUE(transposed_val) TYPE REF TO if_array_element.
      
      
      METHOD lazy_transpose.
        TRY.
            transposed_val = arr[ y ][ x ]. " just swap the indexes
          CATCH cx_root INTO DATA(cx).
            " support for non-square arrays is planned for next release
        ENDTRY.
      ENDMETHOD.

      Because there are no clever shortcuts to transposing an array (it requires accessing all n^2 elements), don't do it at all if you don't need to. Just get the value from where it would be if the array was transposed.

      Even lazier...

      By transposing a row-store database, you get a column-store database. Your data has been pre-transposed for optimal performance since HANA was released in 2010. Transposing it again would be counterproductive.

      Author's profile photo Matthew Billingham
      Matthew Billingham

      Well, the first suggestion was just to propose a different way. I like the solution of not transposing an array. It could be implemented something like this:

      METHOD transpose_matrix.
        toggle transposed flag.
      ENDMETHOD.
      
      METHOD get_element.
        if transposed.
          return element at y x
        else
          return element at x y.
      ENDMETHOD.
      Author's profile photo Jacques Nomssi Nzali
      Jacques Nomssi Nzali

      Hello Akhilesh,

      thanks for taking the time to document your ABAP experience with transposition.

      The Rosetta Code repository has solutions in different programming languages. I could not help but try the Scheme solution using the abapScheme workbench. The logic is just:

      (define (transpose m)
        (apply map list m))

      It looks like cheating, doesn't it?

      (display (transpose '((1 1 1) 
                            (2 2 2) 
                            (3 3 3))))
      => ((1 2 3) (1 2 3) (1 2 3))
      
      (display (transpose '((1 2 3 4 5))))
      => ((1) (2) (3) (4) (5))

      The insight for me is that a generic transpose should represent matrices using a tree based data structure (S-Expression).

      best regards,

      JNN