ABAP Deep Dive - FOR (Loops)
Let's take a closer look at the FOR loop. How does it work? What do I have to consider and what can I do with it?
Table of contents
With the introduction of new statements in the ABAP language, an addition for the loop was also developed. You can use FOR to map a loop to a line to iterate over data. In the following sections we will take a closer look at the manifestations of this form.
Introduction
One thing has to be said in advance, the FOR loop is not a substitute for the LOOP. It only works within the statements REDUCE, NEW and VALUE, so-called constructor expressions. In the FOR loop, you cannot add arbitrary statements, as is the case in the LOOP.
Preparation
Before we start with the examples, let's create the appropriate structures that we will work with in the examples. The structures are defined as follows:
TYPES:
td_key TYPE c LENGTH 5,
BEGIN OF ts_simple_data,
tkey TYPE td_key,
text TYPE string,
number TYPE i,
date TYPE d,
END OF ts_simple_data,
tt_simple_data TYPE STANDARD TABLE OF ts_simple_data WITH EMPTY KEY,
BEGIN OF ts_easy_data,
tkey TYPE td_key,
text TYPE string,
number TYPE i,
END OF ts_easy_data,
tt_easy_data TYPE STANDARD TABLE OF ts_easy_data WITH EMPTY KEY,
tt_numbers TYPE STANDARD TABLE OF i WITH EMPTY KEY.
To do this, we implement a method that returns a set of test data:
METHOD generate_data.
rt_result = VALUE #(
( tkey = 'A' text = `Banana` number = 14 date = '20230101' )
( tkey = 'B' text = `Tomato` number = 12 date = '20230201' )
( tkey = 'C' text = `Apple` number = 23 date = '20231201' )
( tkey = 'E' text = `Strawberry` number = 31 date = '20230101' )
( tkey = 'F' text = `Salad` number = 20 date = '20230601' )
).
ENDMETHOD.
Count variable
You will certainly know the counting loop from other programming languages. A loop that tells you to count from where to where and then increase the number accordingly. To do this, let's create an empty table with numbers. In the VALUE expression we now implement the loop and start with FOR, then the variable and the start value and up to where we want to count. Finally, the structure to be created is placed in brackets:
DATA(lt_numbers_until) = VALUE tt_numbers(
FOR i = 1 UNTIL i >= 10 ( i )
).
In addition to the UNTIL statement, there is also WHILE, depending on what you prefer to use. An example of this type of loop looks something like this:
DATA(lt_numbers_while) = VALUE tt_numbers(
FOR i = 1 WHILE i <= 10 ( i )
).
If nothing is specified, then the variable is increased by +1, but here there is also the possibility to adjust the counting after THEN. In the following example we increase the counter by +3:
DATA(lt_numbers_then) = VALUE tt_numbers(
FOR i = 1 THEN i + 3 WHILE i <= 10 ( i )
).
The output from the three loops looks like this:
Data type
In the above examples we have always counted with integers, but other data types such as date (D), time (T) or numeric (N) are also possible. Next, an example for a counter variable with a date, we only include every odd day in our new table:
DATA(lt_date_variable) = VALUE tt_simple_data(
FOR i = CONV d( '20230101' ) THEN i + 2 WHILE i <= '20230201' ( date = i )
).
In order to create the correct data type, we convert the value into the variable, the calculation can handle the date field afterwards. The result of the table then looks like this:
FOREACH
In other programming languages, the loop is called FOREACH. This loop is about processing all the records in a table. You could do this with a COUNT and a counting loop, but why make it complicated when it can be easy. First of all we need a base table:
DATA(lt_base) = generate_data( ).
In the next step we build two loops, one loop that does a simple transformation with CORRESPONDING and a second loop that takes the fields but treats some fields differently:
DATA(lt_corresponding) = VALUE tt_easy_data(
FOR ls_base IN lt_base ( CORRESPONDING #( ls_base ) )
).
DATA(lt_simple_mapping) = VALUE tt_easy_data(
FOR ls_base IN lt_base ( tkey = ls_base-tkey text = ls_base-text number = 1 )
).
The result of the two loops looks like this:
WHERE
Similar to the LOOP, we also have the option of restricting the data and delimiting it based on the content. In the following example, we restrict the data using the date, we also have the option of providing the index of the current line in the loop:
DATA(lt_where) = VALUE tt_easy_data(
FOR ls_base IN lt_base INDEX INTO ld_idx WHERE ( date > '20230101' )
( tkey = ls_base-tkey text = ls_base-text number = ld_idx )
).
We transfer the row to the new data type and set the "number" field to the index of the source row so that we can find the data record again later. The result after the loop now looks like this:
REDUCE
As a final example, let's take a look at the REDUCE statement, which is intended to reduce or summarize content. It is also possible to use the FOR loop here. To do this, we will look at three different scenarios and how they can be used.
First, we use a counting loop and sum the square of the counting variable together. We define a local variable with INIT, here you can create different variables for calculation and storage, but the first variable is decisive, the data type of which should be mappable to the result:
DATA(ld_number) = REDUCE i(
INIT num = 0
FOR i = 1 THEN i + 3 WHILE i <= 10
NEXT num = num + ( i * i )
).
In the second example, we sum all the numbers in our base table and return the result from the REDUCE:
DATA(ld_sum) = REDUCE i(
INIT sum = 0
FOR ls_base IN lt_base
NEXT sum = sum + ls_base-number
).
As a last example, we don't add anything together, but rather compose a text from the contents of columns in the table. For this it is important that the variable is initialized with the string literal, otherwise it will be defined as CHAR1 and the concatenation will not work:
DATA(ld_keys) = REDUCE string(
INIT keys = ``
FOR ls_base IN lt_base
NEXT keys = keys && ls_base-tkey
).
Finally, the output from the three examples and what they would look like in the console:
Full example
As always, finally, the full executable class we used for the examples:
CLASS zcl_bs_demo_for DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
TYPES:
td_key TYPE c LENGTH 5,
BEGIN OF ts_simple_data,
tkey TYPE td_key,
text TYPE string,
number TYPE i,
date TYPE d,
END OF ts_simple_data,
tt_simple_data TYPE STANDARD TABLE OF ts_simple_data WITH EMPTY KEY,
BEGIN OF ts_easy_data,
tkey TYPE td_key,
text TYPE string,
number TYPE i,
END OF ts_easy_data,
tt_easy_data TYPE STANDARD TABLE OF ts_easy_data WITH EMPTY KEY,
tt_numbers TYPE STANDARD TABLE OF i WITH EMPTY KEY.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS:
generate_data
RETURNING VALUE(rt_result) TYPE tt_simple_data,
simple_for_with_counter
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out,
simple_for_with_mapping
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out,
for_with_reduce
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out,
for_with_where_condition
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out,
simple_for_with_date
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zcl_bs_demo_for IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
simple_for_with_counter( out ).
simple_for_with_date( out ).
simple_for_with_mapping( out ).
for_with_reduce( out ).
for_with_where_condition( out ).
ENDMETHOD.
METHOD generate_data.
rt_result = VALUE #(
( tkey = 'A' text = `Banana` number = 14 date = '20230101' )
( tkey = 'B' text = `Tomato` number = 12 date = '20230201' )
( tkey = 'C' text = `Apple` number = 23 date = '20231201' )
( tkey = 'E' text = `Strawberry` number = 31 date = '20230101' )
( tkey = 'F' text = `Salad` number = 20 date = '20230601' )
).
ENDMETHOD.
METHOD simple_for_with_counter.
DATA(lt_numbers_until) = VALUE tt_numbers(
FOR i = 1 UNTIL i >= 10 ( i )
).
io_out->write( `Result table with NUMBERS (UNTIL):` ).
io_out->write( lt_numbers_until ).
DATA(lt_numbers_while) = VALUE tt_numbers(
FOR i = 1 WHILE i <= 10 ( i )
).
io_out->write( `Result table with NUMBERS (WHILE):` ).
io_out->write( lt_numbers_while ).
DATA(lt_numbers_then) = VALUE tt_numbers(
FOR i = 1 THEN i + 3 WHILE i <= 10 ( i )
).
io_out->write( `Result table with NUMBERS (THEN):` ).
io_out->write( lt_numbers_then ).
ENDMETHOD.
METHOD simple_for_with_mapping.
DATA(lt_base) = generate_data( ).
DATA(lt_corresponding) = VALUE tt_easy_data(
FOR ls_base IN lt_base ( CORRESPONDING #( ls_base ) )
).
io_out->write( `Result table with CORRESPONDING:` ).
io_out->write( lt_corresponding ).
DATA(lt_simple_mapping) = VALUE tt_easy_data(
FOR ls_base IN lt_base ( tkey = ls_base-tkey text = ls_base-text number = 1 )
).
io_out->write( `Result table with simple mapping:` ).
io_out->write( lt_simple_mapping ).
ENDMETHOD.
METHOD for_with_reduce.
DATA(lt_base) = generate_data( ).
DATA(ld_number) = REDUCE i(
INIT num = 0
FOR i = 1 THEN i + 3 WHILE i <= 10
NEXT num = num + ( i * i )
).
io_out->write( |Result from table reduce: { ld_number }| ).
DATA(ld_sum) = REDUCE i(
INIT sum = 0
FOR ls_base IN lt_base
NEXT sum = sum + ls_base-number
).
io_out->write( |Sum of all items: { ld_sum }| ).
DATA(ld_keys) = REDUCE string(
INIT keys = ``
FOR ls_base IN lt_base
NEXT keys = keys && ls_base-tkey
).
io_out->write( |All keys concatenated: { ld_keys }| ).
ENDMETHOD.
METHOD for_with_where_condition.
DATA(lt_base) = generate_data( ).
DATA(lt_where) = VALUE tt_easy_data(
FOR ls_base IN lt_base INDEX INTO ld_idx WHERE ( date > '20230101' )
( tkey = ls_base-tkey text = ls_base-text number = ld_idx )
).
io_out->write( `Result with date greater than 01/01/2023:` ).
io_out->write( lt_where ).
ENDMETHOD.
METHOD simple_for_with_date.
DATA(lt_date_variable) = VALUE tt_simple_data(
FOR i = CONV d( '20230101' ) THEN i + 2 WHILE i <= '20230201' ( date = i )
).
io_out->write( `Result table with date type:` ).
io_out->write( lt_date_variable ).
ENDMETHOD.
ENDCLASS.
Conclusion
Loops with FOR can already be used today, but do not necessarily improve readability. Furthermore, they are not a substitute for LOOPs, since they only work in given statements. Nevertheless, the new construct should be mastered.
Source:
ABAP Documentation - FOR Iteration Expressions
ABAP Documentation - FOR Conditional Iteration