Matrix in Abap: Complete Solution & Deep Dive Guide

a close up of a computer screen with code on it

Mastering ABAP Matrices: The Ultimate Guide to Data Transformation

Learn to expertly parse a string-based number matrix in ABAP. This comprehensive guide covers transforming a newline-separated string into structured rows and columns using modern ABAP syntax, internal tables, and robust looping techniques, a fundamental skill for advanced data processing and reporting in any SAP environment.


Ever found yourself staring at a single, monolithic string of data in an SAP system and wondering how to turn that chaos into order? Perhaps you've received data from a flat file upload, a web service, or even a custom long text field, formatted like a table but trapped within a single string variable. It's a common pain point for ABAP developers: the challenge of transforming unstructured text into a structured, usable format like an internal table.

This is where the real power of ABAP's data handling capabilities comes into play. The ability to deconstruct, process, and reconstruct data is a cornerstone of building powerful and flexible applications. In this guide, we will embark on a deep dive into solving this exact problem. We'll take a string representation of a numerical matrix and systematically build a robust ABAP class to extract its rows and columns, turning messy data into a clean, two-dimensional structure you can actually work with.

What is a Matrix in the Context of ABAP?

In mathematics, a matrix is a rectangular array of numbers arranged in rows and columns. In programming, and specifically in ABAP, we don't have a native "matrix" data type. Instead, we simulate this two-dimensional structure using one of ABAP's most powerful tools: internal tables.

An ABAP matrix is typically implemented as a "table of tables." Imagine an outer internal table where each row of this table is, itself, another internal table. The outer table represents the rows of the matrix, and each inner table represents the columns within that specific row.

To define such a structure, we use nested TYPE declarations. For a matrix of integers, the definition would look like this:


" Defines a single row, which is a table of integers.
TYPES: ty_row TYPE STANDARD TABLE OF i WITH EMPTY KEY.

" Defines the matrix, which is a table of rows.
TYPES: ty_matrix TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY.

" Now we can declare a data object of this type.
DATA: gt_my_matrix TYPE ty_matrix.

This gt_my_matrix variable is now capable of holding our two-dimensional data. Accessing an element, say in the 2nd row and 3rd column, would be done with an expression like gt_my_matrix[ 2 ][ 3 ] in modern ABAP syntax, which is far more intuitive than the older READ TABLE ... INDEX ... statements.


Why is Matrix Manipulation a Crucial Skill in SAP?

You might think that matrix manipulation sounds like a purely academic exercise, but it's a surprisingly practical skill with numerous real-world applications in the SAP ecosystem. Business processes are complex, and data rarely arrives in the perfect format. The ability to parse and restructure data is essential for integration, reporting, and automation.

  • Data Migration & Uploads: When migrating data from legacy systems or performing bulk uploads using tools like LSMW or custom upload programs, data often comes in CSV or other delimited text formats. Your ABAP program needs to parse these strings line-by-line and field-by-field, a process identical to parsing a matrix from a string.
  • Interfacing with External Systems: APIs and external services don't always communicate in perfectly structured XML or JSON. Sometimes, you'll receive data payloads in plain text formats that need to be deconstructed into an internal table structure before they can be processed by BAPIs or other SAP function modules.
  • Dynamic ALV Grid Reporting: Imagine building a complex report where the number of columns is not fixed but determined at runtime based on user input or data. You might build the data dynamically in a matrix-like internal table before passing it to an ALV grid factory method like cl_salv_table=>factory.
  • Complex Calculations: In modules like Finance (FI/CO) or Production Planning (PP), you may need to perform calculations across rows and columns of data. For example, calculating cross-period totals or analyzing production variances requires treating your data as a logical matrix.

Mastering this technique moves you from being a developer who can only work with pre-formatted data to one who can handle the messy, real-world data challenges that define complex enterprise projects.


How to Implement the Matrix Solution in ABAP

Now, let's get to the core of the solution. Our goal is to create a reusable, object-oriented ABAP class that can take a matrix string as input and provide methods to retrieve the data as a collection of rows or a collection of columns. This approach encapsulates the logic cleanly and makes it easy to use in any ABAP program.

The overall logic can be broken down into two main phases:

  1. Parsing Rows: We'll read the input string, split it into lines to get the individual rows, and then split each line into numbers to populate our main matrix structure.
  2. Transposing for Columns: Once we have the rows, we'll implement a transposition algorithm. This involves creating a new matrix structure and populating it by iterating through the original matrix column by column instead of row by row.

The Core Logic: Parsing and Storing Rows

The first step is to deconstruct the input string. The string contains numbers separated by spaces, and rows separated by newline characters. ABAP's SPLIT statement is the perfect tool for this job.

Here is a conceptual flow of how we'll process the input string to build our row-based internal table.

  ● Start with Input String
    "9 8 7\n5 3 2\n6 6 7"
    │
    ▼
  ┌─────────────────────────────┐
  │ SPLIT at Newline (\n)       │
  └─────────────┬───────────────┘
                │
                │ lt_row_strings
                ├─> [ "9 8 7", "5 3 2", "6 6 7" ]
                │
    ┌───────────┴───────────┐
    │ LOOP through each row │
    │ string                │
    └───────────┬───────────┘
   (For "9 8 7")│
                ▼
      ┌─────────────────────────┐
      │ SPLIT row at Space (' ')│
      └───────────┬─────────────┘
                  │
                  │ lt_number_strings
                  ├─> [ "9", "8", "7" ]
                  │
        ┌─────────┴─────────┐
        │ Convert strings   │
        │ to integers       │
        └─────────┬─────────┘
                  │
                  │ lt_current_row (integers)
                  ├─> [ 9, 8, 7 ]
                  │
                  ▼
      ┌─────────────────────────┐
      │ APPEND to final matrix  │
      └─────────────────────────┘
                  │
                  ▼
           Repeat for all rows
                  │
                  ▼
  ● End with structured Row Matrix
    [ [9,8,7], [5,3,2], [6,6,7] ]

The ABAP Class Implementation

Let's build the complete ABAP class. We'll name it zcl_matrix. This class will handle all the logic internally. The public interface will be simple: a constructor to receive the string, and two methods, get_rows and get_columns.

This code is written using modern ABAP syntax (available on SAP S/4HANA and recent NetWeaver stacks). If you are on an older system, you may need to replace inline declarations (DATA(...)) and constructor expressions (VALUE #...) with their older equivalents.


CLASS zcl_matrix DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    "! A single row, represented as a table of integers
    TYPES:
      ty_row TYPE STANDARD TABLE OF i WITH EMPTY KEY.
    "! The matrix, represented as a table of rows
    TYPES:
      ty_matrix TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY.

    "! Constructor: Parses the input string into a matrix of rows
    "! @parameter iv_matrix_string | The input string with newlines
    METHODS constructor
      IMPORTING
        iv_matrix_string TYPE string.

    "! Returns the matrix as a table of rows
    "! @parameter rt_rows | The matrix organized by rows
    METHODS get_rows
      RETURNING
        VALUE(rt_rows) TYPE ty_matrix.

    "! Returns the matrix as a table of columns (transposed)
    "! @parameter rt_columns | The matrix organized by columns
    METHODS get_columns
      RETURNING
        VALUE(rt_columns) TYPE ty_matrix.

  PRIVATE SECTION.
    DATA mt_rows TYPE ty_matrix.
    DATA mt_columns TYPE ty_matrix.

ENDCLASS.

CLASS zcl_matrix IMPLEMENTATION.

  METHOD constructor.
    " This constructor is the heart of the parsing logic.
    " It takes the raw string and builds the primary `mt_rows` internal table.

    " 1. Split the entire input string by the newline character.
    "    This gives us a table of strings, where each string is one row.
    SPLIT iv_matrix_string AT cl_abap_char_utilities=>cr_lf INTO TABLE DATA(lt_row_strings).

    " 2. Loop through each row string we just extracted.
    LOOP AT lt_row_strings INTO DATA(lv_row_string).
      " Skip any empty lines that might result from trailing newlines.
      IF lv_row_string IS INITIAL.
        CONTINUE.
      ENDIF.

      " 3. For each row string, split it by spaces to get individual number strings.
      SPLIT lv_row_string AT ' ' INTO TABLE DATA(lt_number_strings).

      " 4. Create a temporary table to hold the integers for the current row.
      DATA(lt_current_row) = VALUE ty_row( ).

      " 5. Loop through the number strings, convert them to integers, and append.
      LOOP AT lt_number_strings INTO DATA(lv_number_string).
        " Using a `TRY...CATCH` block is robust for handling non-numeric input.
        TRY.
            APPEND CONV i( lv_number_string ) TO lt_current_row.
          CATCH cx_sy_conversion_error.
            " In a real application, you would raise an exception or log an error.
            " For this example, we'll just skip invalid entries.
            CONTINUE.
        ENDTRY.
      ENDLOOP.

      " 6. Once the row is fully parsed into integers, append it to our main class attribute.
      APPEND lt_current_row TO me->mt_rows.

    ENDLOOP.
  ENDMETHOD.

  METHOD get_rows.
    " This is a simple getter method. The real work was done in the constructor.
    rt_rows = me->mt_rows.
  ENDMETHOD.

  METHOD get_columns.
    " This method performs the matrix transposition.

    " Optimization: If we've already calculated the columns, just return them.
    IF me->mt_columns IS NOT INITIAL.
      rt_columns = me->mt_columns.
      RETURN.
    ENDIF.

    " Check for an empty matrix to avoid errors.
    IF me->mt_rows IS INITIAL.
      RETURN.
    ENDIF.

    " Determine the dimensions of the matrix.
    " We assume a non-jagged matrix (all rows have the same number of columns).
    DATA(lv_row_count) = lines( me->mt_rows ).
    DATA(lv_col_count) = lines( me->mt_rows[ 1 ] ).

    " Outer loop: Iterate from the first column to the last.
    DO lv_col_count TIMES.
      DATA(lv_current_col_idx) = sy-index. " Current column index (1-based)

      " Create a temporary table to build the current column.
      DATA(lt_new_column) = VALUE ty_row( ).

      " Inner loop: Iterate through each row to pick up the element for the current column.
      DO lv_row_count TIMES.
        DATA(lv_current_row_idx) = sy-index. " Current row index (1-based)

        " Read the element at [row_index][col_index] from the source matrix.
        DATA(lv_element) = me->mt_rows[ lv_current_row_idx ][ lv_current_col_idx ].

        " Append this element to our new column table.
        APPEND lv_element TO lt_new_column.
      ENDDO.

      " After iterating through all rows, the new column is complete. Append it.
      APPEND lt_new_column TO me->mt_columns.
    ENDDO.

    rt_columns = me->mt_columns.
  ENDMETHOD.

ENDCLASS.

Code Walkthrough: The Transposition Logic

The get_columns method is where the magic of transposition happens. It converts our row-oriented table into a column-oriented one. Let's visualize this process. If our row matrix (`mt_rows`) looks like this:

[
  [9, 8, 7],  <-- Row 1
  [5, 3, 2],  <-- Row 2
  [6, 6, 7]   <-- Row 3
]

The algorithm builds the new column matrix (`mt_columns`) step-by-step.

  ● Start with Row Matrix
    [ [9,8,7], [5,3,2], [6,6,7] ]
    │
    ▼
  ┌─────────────────────────────┐
  │ Outer Loop (By Column Index)│
  │ sy-index = 1                │
  └─────────────┬───────────────┘
                │
      ┌─────────┴─────────┐
      │ Inner Loop (By Row) │
      └─────────┬─────────┘
                │
                ├─> Read rows[1][1] → 9
                ├─> Read rows[2][1] → 5
                ├─> Read rows[3][1] → 6
                │
                ▼
      ┌─────────────────────────┐
      │ Build First Column      │
      │ [9, 5, 6]               │
      └─────────┬───────────────┘
                │
                ▼
  ┌─────────────────────────────┐
  │ Outer Loop (By Column Index)│
  │ sy-index = 2                │
  └─────────────┬───────────────┘
                │
      ┌─────────┴─────────┐
      │ Inner Loop (By Row) │
      └─────────┬─────────┘
                │
                ├─> Read rows[1][2] → 8
                ├─> Read rows[2][2] → 3
                ├─> Read rows[3][2] → 6
                │
                ▼
      ┌─────────────────────────┐
      │ Build Second Column     │
      │ [8, 3, 6]               │
      └─────────────────────────┘
                │
                ▼
          ... and so on ...
                │
                ▼
  ● End with Column Matrix
    [ [9,5,6], [8,3,6], [7,2,7] ]

The nested DO...TIMES loops are a classic and clear way to implement this. The outer loop iterates as many times as there are columns. The inner loop iterates as many times as there are rows. Inside the inner loop, we use the loop counters (sy-index) as coordinates to pick the correct element from the source `mt_rows` table and place it into our temporary column table.


Where is This Pattern Used in Real SAP Projects?

The logic encapsulated in our zcl_matrix class is a foundational pattern for data transformation. While you might not be building a class named "matrix" every day, the underlying techniques are ubiquitous.

  • Parsing User Input from Text Controls: Imagine a transaction screen (Dynpro) or a Web Dynpro application with a large text box where a user can paste data from Excel. This data will arrive in your ABAP backend as a single string with tabs and newlines. Our class logic is perfectly suited to parse this input into a structured internal table for validation and processing.
  • Handling SAPScript/Smartforms Long Text: Data stored in long text objects (like purchase order header texts) is often retrieved as a table of strings (`tline`). You might need to parse these lines if they contain structured, delimited data that a user has entered.
  • -
  • Custom IDoc Segment Parsing: While standard IDocs are well-structured, in custom developments, a single long segment field (`sdata`) is sometimes used to carry multiple key-value pairs or delimited data to save on segment creation. Your processing function module would need to parse this string field into a usable format.

By understanding this pattern, you are better equipped to build bridges between the unstructured and structured data worlds within SAP, a skill that is invaluable for creating robust and user-friendly applications. For more advanced ABAP topics, you can explore our complete guide to ABAP programming.


When to Choose Alternative Approaches?

While the SPLIT statement is excellent for clearly defined delimiters, it's not the only tool available. Depending on the complexity and format of the input string, other approaches might be more suitable.

Alternative 1: Using Regular Expressions (Regex)

If the input format is more complex—for example, with variable amounts of whitespace between numbers—a regular expression can be more flexible. You could use the FIND ALL OCCURRENCES OF REGEX statement to find all sequences of digits (\d+) and handle the structure based on the line breaks found separately.

Alternative 2: Fixed-Width Parsing

In some legacy or mainframe integration scenarios, data doesn't use delimiters. Instead, it's formatted in fixed-width columns. For example, the first number is always characters 1-5, the second is 6-10, and so on. In this case, you wouldn't use SPLIT. Instead, you would loop through the string and use offset/length slicing (e.g., lv_row_string+0(5), lv_row_string+5(5)) to extract the values.

Pros & Cons Comparison

Let's compare these approaches for our specific problem.

Approach Pros Cons
SPLIT Statement
  • Highly readable and easy to understand.
  • Excellent performance for simple delimiters.
  • The standard, idiomatic way to solve this problem in ABAP.
  • Less flexible if delimiters are inconsistent (e.g., tabs and spaces mixed).
  • Struggles with formats that are not strictly delimiter-separated.
Regular Expressions
  • Extremely powerful and flexible for complex patterns.
  • Can handle variable whitespace and more complex validation rules.
  • Significantly slower performance than SPLIT.
  • Can be difficult to read and maintain ("regex-golf").
  • Overkill for simple, well-defined delimiter problems.
Fixed-Width Slicing
  • Very fast performance.
  • The only correct way to handle fixed-width data.
  • Completely unsuitable for delimited data.
  • Brittle; if the format changes, the code breaks.

For the problem defined in the kodikra learning path, the SPLIT statement is the clear winner due to its perfect balance of readability, performance, and suitability for the task.


Frequently Asked Questions (FAQ)

1. How should I handle non-numeric values in the input string?

The provided solution includes a TRY...CATCH cx_sy_conversion_error block. This is the most robust way to handle this. In a real-world application, you shouldn't just CONTINUE silently. You should implement proper error handling, such as collecting the errors in a log table and returning it to the caller, or raising a custom exception to signal that the input data was invalid.

2. What happens if the rows have a different number of columns (a "jagged" matrix)?

The current solution assumes a uniform matrix. The line DATA(lv_col_count) = lines( me->mt_rows[ 1 ] ) would cause issues. If you need to support jagged matrices, the transposition logic in get_columns would need to be more complex. You would first need to determine the maximum number of columns across all rows and then, during transposition, check if an element exists at a given coordinate before trying to access it.

3. Can this logic be used in an AMDP or CDS View?

No. This is purely application layer logic written in ABAP. AMDPs (ABAP Managed Database Procedures) are for executing code directly on the HANA database using SQLScript, and CDS Views are for data modeling. String parsing and procedural logic like this belong in your ABAP classes and reports, which run on the SAP application server.

4. What is the performance impact of the nested loops for very large matrices?

The time complexity of the transposition algorithm is O(n*m), where 'n' is the number of rows and 'm' is the number of columns. This is generally efficient and unavoidable, as you must visit every element at least once. For extremely large datasets (millions of cells), performance could become a factor, but for typical business scenarios in SAP, this approach is perfectly acceptable and performant.

5. How can I adapt this code for comma-separated values (CSV)?

It's a very simple change. In the constructor method, you would modify the second SPLIT statement. Instead of splitting by a space, you would split by a comma: SPLIT lv_row_string AT ',' INTO TABLE lt_number_strings. This highlights the flexibility of the core pattern.

6. Is there a single built-in ABAP function to transpose an internal table?

No, there is no single statement like TRANSPOSE itab1 INTO itab2 in ABAP. The nested loop algorithm we implemented in the get_columns method is the standard, accepted way to perform a matrix transposition. This is a common programming exercise because it tests a developer's understanding of loops and multi-dimensional data structures.


Conclusion and Next Steps

We have successfully built a complete, object-oriented solution in ABAP to parse a string-based matrix and access its rows and columns. This journey took us through fundamental ABAP concepts including nested internal tables, the SPLIT statement, modern ABAP syntax, and the classic matrix transposition algorithm.

The key takeaway is that complex data transformation problems can be broken down into smaller, manageable steps. By encapsulating this logic within a class, we've created a reusable and maintainable component that can be leveraged across various applications. This pattern of parsing and restructuring data is a vital skill for any serious ABAP developer working on the complex integration and reporting challenges found in SAP environments.

This module is a stepping stone in your journey. To see how this skill fits into the bigger picture, we encourage you to explore the full ABAP Learning Path on kodikra.com and continue building your expertise.

Disclaimer: The ABAP code in this article is written for modern SAP S/4HANA systems (e.g., 2020 and newer). Syntax such as inline declarations and constructor expressions may need to be adapted for older SAP NetWeaver versions.


Published by Kodikra — Your trusted Abap learning resource.