Skip to Content

ABAP News for 7.40, SP08 – FOR Expressions

With 7.40, SP05 the first version of the iteration operator FOR was introduced. You can use it in constructor expressions with VALUE and NEW for so called table comprehensions, as e.g.

DATA(itab2) = VALUE t_itab2( FOR wa IN itab1 WHERE ( col1 < 30 )

                             ( col1 = wa-col2 col2 = wa-col3 ) ).

This is an expression enabled version of LOOP AT itab. It didn’t take long to ask also for expression enabled versions of DO and WHILE (couldn’t stand them in otherwise expression enabled examples any more …).

Therefore, with 7.40, SP08 we also offer conditional iterations with FOR:

… FOR i = … [THEN expr] UNTIL|WHILE log_exp …

You can use FOR in constructor expressions with VALUE and NEW in order to create new internal tables, e.g.:

  BEGIN OF line,
    col1 TYPE i,
    col2 TYPE i,
    col3 TYPE i,
  END OF line,

DATA(itab) = VALUE itab(
     FOR j = 11 THEN j + 10 UNTIL j > 40
     ( col1 = j col2 = j + 1 col3 = j + 2  ) ).


11 12 13
21 22 23
31 32 33

Neat, isn’t it?

But we don’t want to construct internal tables only. Now that we have all kinds of iterations available with FOR, we want to construct arbitrary types. And that’s where the new constructor operator REDUCE comes in.

… REDUCE type(

      INIT result = start_value


      FOR for_exp1

      FOR for_exp2


      NEXT …

           result = iterated_value

           … ) …

While VALUE and NEW expressions can include FOR expressions, REDUCE must include at least one FOR expression. You can use all kinds of FOR expressions in REDUCE:

  • with IN for iterating internal tables
  • with UNTIL or WHILE for conditional iterations.

Let’s reduce an internal table:

itab = VALUE #( FOR j = 1 WHILE j <= 10 ( j ) ).

DATA(sum) = REDUCE i( INIT x = 0 FOR wa IN itab NEXT x = x + wa ).

First, the table is filled with VALUE and FOR and then it is reduced with REDUCE to the sum of its contents. Note that there is no THEN used to construct the table. If THEN is not specified explicitly, implicitly THEN j = j + 1 is used. Be also aware, that you can place any expression behind THEN, including method calls. You only have to make sure that the end condition is reached within maximal program run time.

Now let’s reduce the values of a conditional iteration into a string:

DATA(result) =

  REDUCE string( INIT text = `Count up:`

                 FOR n = 1 UNTIL n > 10

                 NEXT text = text && | { n }| ).

The result is

Count up: 1 2 3 4 5 6 7 8 9 10

These simple examples show the principle. Now imagine, what you can do by mixing REDUCE with all the other expression enabled capabilities. I only say nested REDUCE and VALUE operators …

To conclude I show a cunning little thing that I use in some of my documentation examples:

TYPES outref TYPE REF TO if_demo_output.

DATA(output) =
  REDUCE outref( INIT out  = cl_demo_output=>new( )
                      text = `Count up:`
                 FOR n = 1 UNTIL n > 11
                 NEXT out = out->write( text )
                      text = |{ n }| ).

output->display( ).

I reduced the values of an iteration into the output list of a display object, oh my …

You must be Logged on to comment or reply to a post.
  • Hello Horst,


    today i came across an error with a FOR expression that i can’t explain.


    Considering the following code everything is fine and the internal table chars is filled with ‘T’, ‘e’, ‘s’ and ‘t’.

    DATA: text TYPE string VALUE ‘Test’.


    DATA(chars) = VALUE stringtab( FOR off = 0 THEN off + 1

                                                            WHILE off < strlen( text )

                                                                   ( |{ text+off(1) }| ) ).


    I if do it i a similar way like this, i get a STRING_OFFSET_TOO_LARGE runtime error saying that the string was accessed with offset 4.

    DATA(chars) = VALUE stringtab( FOR off = 0 THEN off + 1

                                                           WHILE off < strlen( text )

                                                                   ( text+off(1) ) ). “<– Difference here

    Why is that the case? Where does the offset 4 come from? The only difference is that the substring access is wrapped with a string template.


    Thanks for the clarification.



  • Hello Horst,


    next one .

    Considering the following code which sums the multiples of a number (3) up to a given limit (1000).


    TYPES: ty_int_hash_table TYPE HASHED TABLE OF i

                                 WITH UNIQUE KEY table_line.


    DATA(multiples) =

              REDUCE ty_int_hash_table( INIT values =

                                                                          VALUE ty_int_hash_table( )

                                                             FOR i = 0 THEN i + 3 WHILE i < 1000

                                                             NEXT values = VALUE #( BASE values

                                                                                                              ( i ) ) ).




    DATA(sum_of_multiples) = REDUCE i( INIT result = 0

                                                                    FOR value IN multiples

                                                                    NEXT result = result + value ).

    This works fine and sum_of_multiples contains the right value of 166833. If i try to inline the first reduce statement and replace the multiples variable, the compiler complains saying “Error in STRUC-Stack”.


    2015-04-15 17_37_27-ABAP - Programm Z_TEST_SUM_OF_MULTIPLES [DV2] - Aktiv, Gesperrt - DV2_010_guente.png


    What means this error? Can’t i use REDUCE multiple times in one expression? Or does this error tell me that the expression is too complex like the infamous REGEX_TOO_COMPLEX runtime error? Or is there a subtle error in the code?


    Thanks for the clarification.


    Regards Christian

  • Hallo Horst,


    I have tried the example with the reduced string from the ABAP Docs, but with a little extension: at the NEXT position I have added a COND construct, like this.


    The scope of the statement is to filter any not allowed characters from a string.REDUCE_W_FOR_AND_COND.PNG

    The problem here is that the iteration runs only once

    What is wrong with the construct?


    Thanks Attila

      • Yeap

        I have seen that latter in debugger

        After the right statement was implemented, it worked as planned(and after the string pattern was right defined).

        Now I am comparing the runtimes with the “old” construct DO. IF. ENDIF.ENDDO. because this check must run in production over many many records.



        I have one more question that is not to the subject related:

        how can I define the literal “space” in string pattern. Now it is so defined:

        regex = `[A-Za-z0-9\’\:\?\,\-\(\+\.\)/“]` (I have the feeling that I miss/overseen something here)


        Many thanks in advance

          • I do have read the docs, thanks for the link. Either with “ nor with \s works


            Another thing to the REDUCE: the String  #*+}Wow My Name should become after that +Wow My Name.

            Instead of that the result looks like  + W o w M y N a m e ,when “result” is with “ initialized.

            where does the space overhead come from?


            Thanks for clarification.



          • Either with “ nor with \s works

            Operator CO does not evaluate regular expressions ….


            where does the space overhead come from?


            From your string template, you have two literal blanks in there ….

  • Dear Team,



    Can you please explain how it’s work i am not able to understand anything..


    Its very difficult to understand, one more question please correct me if i am wrong


    Am i  ABAP blog or in different blog.??




  • Hi Horst


    Is there a way of using table expressions to add values of a column for an entity, eg, add total sales figure for customer line items and store them in an additional column.


    Kind regards

  • Hi Horst


    Very good features coming up with ABAP 7.4

    One qtn regarding FOR operator..

    Why do we need explicit  assignment even if both source and target have same field names

    DATA(itab2) = VALUE t_itab2( FOR wa IN itab1 WHERE ( col1 < 30 )

                                 ( col1 = wa-col1 col2 = wa-col2 ) ).

    if i do just like below I get syntax error

    DATA(itab2) = VALUE t_itab2( FOR wa IN itab1 WHERE ( col1 < 30 ) ).        



  • Hello Horst,


    How does one EXIT a FOR iteration?



    LOOP AT flights ASSIGNING FIELD-SYMBOL(<flight>)

                     WHERE FLDATE <= sy-datum.

      ” do something



    How to build the FOR statement for these lines?




    • What should that “do something” do? You can’t place statements within a FOR expression (OK, calling a method would do).


      There’s nothing like EXIT or CONTINUE, to jump out of a FOR IN itab loop. In a FOR UNTIL loop you can manipulate the iteration variable.


      But: instead of  EXIT you can THROW an exception as workaround:


      DATA(r) = cl_abap_random_int=>create(

         seed = CONV i( syuzeit )

         min  = 0

         max  = 100 ).



                  WITH NON-UNIQUE KEY table_line.

      DATA(itab) = VALUE itab( FOR i = 1

                    UNTIL i > 10

                    ( r->get_next( ) ) ).


      CLASS my_exc DEFINITION INHERITING FROM cx_static_check.



           DATA(jtab) = VALUE itab(

             FOR wa IN itab

             ( COND i( WHEN wa < 50 THEN wa

                        ELSE THROW my_exc( ) ) ) ).

         CATCH my_exc.



      cl_demo_output=>display( jtab ).



      • Suhas approach isn’t that unusual at all because if you need to find records in an internal table and you cannot use an equal condition like FLDATE = SY-DATUM you have to go this way.


        So you cannot use: READ TABLE SFLIGHTS WITH KEY FLDATE <= SY-DATUM to get the first available record that fits this condition although there might be more of them.


        You cannot even access this record using one of those table expression either. Hence you have to use Suhas’ construct to fetch the first record and if found exit immediately.


        I wish at least the new table expressions are way more flexible and support a more generic approach for determining records.



        • Yes, that’s a common use case. We have to use the pattern


          LOOP … WHERE …



          IF sy-subrc = 0.




          because READ does not support WHERE clauses with all kinds of logical conditions.


          But I wonder if this is really a use case for table expressions too . In a FOR loop, you can use fully fledged WHERE clauses.



          • Hi Horst,


            I am facing with the same Problem. I need only the first record that checks the WHERE condition – the pattern you just described. What would be in this case the solution?


            PS: from that record, I need only one field for the further processing.


            Many thanks,


          • Sorry for being a bit late to the party, but why not making

            FOR ... IN itab

            up to par with

            LOOP AT itab
              INTO wa
              WHERE FLDATE <= sy-datum.


            by adding some “EXIT WHEN log_exp”

            ! Beware, just suggesting a command, below is no current ABAP syntax !

            FOR wa IN itab
              WHERE ( FLDATE <= sy-datum )
              EXIT WHEN abap_true
              ( wa ) ).


            At least you have enriched VALUE #( itab[…] OPTIONAL ) in a similar way not forcing exceptions upon us (im mentioning this because one workaround would be throwing an exception).

            However, possibly this is all yet possible with some FOR … THEN … WHILE already, so it might be syntactical sugar and one more command that adds to the already big language scope ABAP did acquire




          • By reading your comment, some people may mistakenly believe that EXIT WHEN exists in ABAP language. Here, you are proposing to the ABAP team of SAP to enhance the ABAP syntax. EXIT WHEN does not exist in ABAP language.

    • Just for fun another and slightly more complicated approach using a reduce expression…




      REPORT z_reduce_for_fun.



          METHODS: start.


          METHODS do_something
              i_flight         TYPE sflight
              c_count          TYPE i
              VALUE(r_flights) TYPE sflight_tab1.


      CLASS lcl_reduce IMPLEMENTATION.

        METHOD start.

          SELECT * FROM sflight
                   INTO TABLE @DATA(flights)
                   UP TO 100 ROWS.

          DATA(reduced_flights) =

                REDUCE sflight_tab1(
                   LET count = 0 IN
                   INIT result = VALUE sflight_tab1( )
                   FOR <flight> IN flights
                   WHERE ( fldate < sy-datum )
                   NEXT result =

                                                    COND #(

                        WHEN count < 1

                          THEN do_something(
                              EXPORTING i_flight = <flight>
                              CHANGING  c_count  = count )
                          ELSE result ) ).

          cl_demo_output=>display( reduced_flights ).


        METHOD do_something.

          ” do something
          c_count = c_count + 1.
          INSERT i_flight INTO TABLE r_flights .



        NEW lcl_reduce( )->start( ).

  • I often have the requirement to copy one internal table into another and add/change some of the target’s table members. Eg:



    Starting from line 60 I tried to replace the coding from lines 51 up to 58, but I had no luck so far.


    Any hints?





    Sorry, for the screenshot. So far I could not paste any coding with syntax highlightning into SCN comments.

    • Hi Michael,


      I would suggest if you can post your query as a question & not as a blog comment. That way if someone is googling for the same topic, (s)he can find it faster.





      PS – When posting a query, you’ll get the syntax highlighting

  • Hello Horst,

    I only work in the solution manager which at last gets all the nice ABAP 7.40 features with the release of version 7.2.

    As far as I´ve been told it wasn´t possible to patch newer ABAP versions into the 7.1 branch, because of some weird technical contraints.

    Do you maybe know, if thats still the case with 7.2 or will it be possible to update the ABAP module to 7.50 and newer once they are available?


    Thanks and regards


    • /
      • Ok thanks for letting me know. I will see if I can find someone who has some details about this.


        Maybe you can answer me some other question:

        Is there already some improvement in Eclipse ADT to provide a better syntax highlighting? Last time I was able to use this (back in 2013) the code looked like in the SE80. But I really would love it, if the SE80 would provide some improvements for that too.


        I think about some special formatting for instance/static method calls or similar things.



        • Hey, that’s a tools, not a language question …


          But as far as I know, they’re constantly improving.


          In 7.51 you will even be able to customize  colors for selfdefined language elements.

          • Hi, thanks for the info. Unfortunately I´m working in the Solution Manager Environment and up to the current version there´s no Eclipse support.

            There will be support in version 7.2 which is currently in ramp up. However I only work in those WTS environments, where the performance of Eclipse is pretty bad. So there is no way around SAP GUI at least for the next couple of years.



  • Hi


    When using REDUCE in the following example the data in my table (MT_DATA)  has amount values like 99.99  – always with 2 decimal places of precision. The data type /POSDW/AMOUNT is defined as CURRENCY (LEN 15, Decimals 2). However the result of the reduction is always an Integer value e.g. 100.


        DATA(lv_amount)  = REDUCE /POSDW/AMOUNT

                                  ( INIT amt = 0 FOR wa IN mt_data
                                    NEXT amt = amt + waamount + wasales_tax_amount ).

    Is this a “feature” of REDUCE or something that I am doing incorrectly?

    All help greatly appreciated.


    The solution turned out to be very easy with a bit of a push in the right direction.


        DATA(lv_amount)  = REDUCE /POSDW/AMOUNT

                                  ( INIT amt = CONV /POSDW/AMOUNT( 0 )

                                    FOR wa IN mt_data
                                    NEXT amt = amt + waamount + wasales_tax_amount



    Thanks Horst and Suhas.

    • Been there, faced that


      Because you used INIT amt = 0, amt is defined(implicitly) as integer(I). If i remember correctly you can check this in the debugger! Try with INIT amt TYPE /POSDW/AMOUNT.

      Just a suggestion, please post such questions as a post and not as a blog comment so that it is easier for others to find




    • In your example, AMT is of type I. Use the CONV operator or TYPE behind INIT to enforce a p type inside the expression.

      • I faced the same issue – using an appropriate initial value for INIT variables also helps:

        This coding behaves erronously (no decimal places):


             REDUCE decfloat16(
                     INIT 0
                     FOR <lv_float> IN lt_floats
                     NEXT + <lv_float>


        This way round it works (decimal places are calculated correctly):


             REDUCE decfloat16(
                     INIT = 0.0′
                     FOR <lv_float> IN lt_floats
                     NEXT + <lv_float>



        Best regards,


          • Let’s call it a decfloat16 literal. However, in my real-world application, I also went for the CONV solution as it caters for potential changes of the corresponding result type specified.

            As I did not notice this issue and solution description when flying through this page for the first time, I added my own experimental code to increase the visibility of this issue in this rather comprehensive collection of issues related to the FOR and REDUCE statements. A more stackoverflow-alike one-issue-at-a-time format with voted solutions would definitely be desireable.

            Regarding the REDUCE statement, I would say, it is not so good at the moment (e.g., there is no reference to this typing issue in the documentation).

          • I totally agree on that.

            Nonetheless I do not find any reference to the CONV statement in it, although I guess it can be considered a best practice to always use it whenever the first helper variable in the INIT block is initialized with a literal (or maybe even in all cases).

          • Well, in your example x is of type c length 3, isn’t it?

            Using INIT x TYPE decfloat16 would be more appropriate and you don’t need CONV in that case.

          • For sure, that would also be a solution for both Niall and my cases.

            However, I still wonder why typing the result value needs to be done twice by the user of the statement, one time as “real” result type and apparently also a second time as dedicated typing of the first INIT variable.

  • Hi Horst,

    While using FOR with WHERE condition i noticed that if the WHERE condition fails then there is no change in the SY-SUBRC, So is there any other way to track the number of rows transferred to the left hand side internal table??? other than the NOT INITIAL check on the internal table.

    Thanks & Regards,

    Rakshith Gore


  • Hi Rakshith Gore,

    Of course an expression should be side effect free and therefore should not influence system fields. Expressions are to be used at operand positions and there you cannot evaluate system fields anyway. If you need the number of rows  selected (where sy-subrc wouldn’t be appropriate anyhow), you must think of other ways, e.g. using the built-in function lines before and after or working with LET expressions in combination with other expressions.




  • Hi

    I am trying to manipulate of one internal tables data depends on other internal table.

    ITAB1 has attributes as zipcode, addresses and more fields

    ITAB 2 has valid Zipcodes


    I have an attribute in ITAB 1, that holds a indicator (ex: Zip Indicator)

    Logic: If ITAB 1 Zipcode is available in ITAB 2 then Attribute ‘ZIP Indicator’ in ITAB 1 should be ‘X’ otherwise space.

    I want all the records from ITAB 1 with indicator updated . (Not a filter)

    My code is below:

    Lt_itab3 = same structure as ITAB 1.


    1. I am getting ‘x’ number of times records (X = lines of ITAB 2) for the above syntax
    2. If i use FOR with Where on ITAB 2, it filters the records

    Please suggest.

  • Is it possible to use a combination of CORRESPONDING and attribute assignments  in the work area for the value transfer of the FOR iteration. Something like below , but i get an error when i write this.



    Hello Horst,

    I often have to update just a few fields in a broad internal table with many columns, the most of them remain unchanged.

    Is there any chance to include the unchanged fields in an easy way in table comprehensions?

    In old school it looks very easy:

    TYPES: gty_sflight TYPE SORTED TABLE OF sflight WITH UNIQUE KEY carrid connid fldate.
    DATA: gt_sflight TYPE gty_sflight.

    “Fill table gt_sflight in any way, e.g. from database table SFLIGHT
    SELECT * FROM sflight INTO TABLE gt_sflight WHERE carrid = ‘LH’ AND connid = ‘400’.

    LOOP AT gt_sflight ASSIGNING FIELD-SYMBOL(<fs_sflight>).

    <fs_sflight>-price = <fs_sflight>-price * 2.
    <fs_sflight>-seatsocc = <fs_sflight>-seatsocc + 30.


    I tried to have this with table comprehensions instead of LOOP …. ENDLOOP, but then I tediously have to enumerate all the unchanged fields in the expression. Is there any chance to default them?

    DATA(gt_sflight_for) = VALUE gty_sflight( LET lt_sflight = gt_sflight IN
    FOR ls_sflight IN lt_sflight
    ( price = ls_sflight-price * 2
    seatsocc = ls_sflight-seatsocc + 30
    “Here I laboriously must enumerate the remaining 12 unchanged fields
    mandt = ls_sflight-mandt
    carrid = ls_sflight-carrid
    connid = ls_sflight-connid
    fldate = ls_sflight-fldate
    currency = ls_sflight-currency
    planetype = ls_sflight-planetype
    seatsmax = ls_sflight-seatsmax
    paymentsum = ls_sflight-paymentsum
    seatsmax_b = ls_sflight-seatsmax_b
    seatsocc_b = ls_sflight-seatsocc_b
    seatsmax_f = ls_sflight-seatsmax_f
    seatsocc_f = ls_sflight-seatsocc_f
    ) ).

    I had two other ideas, but they result in syntax errors as soon I try to change the two fields:

    “Syntax error when adding “* 2” or “+ 30”
    DATA(gt_sflight_for2) = VALUE gty_sflight(
    FOR <l_str_sflight> IN gt_sflight
    LET l_str_sflight2 = <l_str_sflight>
    l_str_sflight2-price = <l_str_sflight>-price * 2
    l_str_sflight2-seatsocc = <l_str_sflight>-seatsocc + 30 IN
    ( l_str_sflight2 ) ).

    “Syntax error when adding “* 2” or “+ 30”
    DATA(gt_sflight_new) = VALUE gty_sflight( LET lt_sflight = gt_sflight IN
    FOR ls_sflight IN lt_sflight
    ( CORRESPONDING sflight( ls_sflight MAPPING price = price * 2 paymentsum = paymentsum +             30 ) ) ).

    Does anyone have any good idea?

    Thank you


  • Hello Horst,

    SPAN {
    font-family: “Courier New”;
    font-size: 10pt;
    color: #000000;
    background: #FFFFFF;
    .L0S32 {
    color: #3399FF;
    .L0S33 {
    color: #4DA619;
    .L0S52 {
    color: #0000FF;
    .L0S55 {
    color: #800080;
    DATA(lv_lenlinespoitem ).
    poitem[] VALUE #(  FOR 10  THEN 10 UNTIL GT lv_len * 10
    po_item i  acctasscat ‘W’ )  .

    In the above syntax i have tried to embed corresponding so that only two fields of table poitem gets filled but was encountered with errors

    DATA(lv_lenlinespoitem ).
    poitem[] VALUE #(  FOR 10  THEN 10 UNTIL GT linespoitem ) * 10
    po_item i  acctasscat ‘W’ )  .

    And also i have tried giving length regex so that it runs for number of table rows but not successful


    So please can you help me in this two points




  • Hi Horst,

    Before anything else, thank you for the great work you guys have been doing.

    I’m really enjoying using the new FOR expression with VALUE, REDUCE, etc. But… it seems to still only work for internal tables. Ideally It would also accept classes that would implement some sort of IF_ITERABLE interface.

    Scenario: class CL_VENDORS holds a list of instances of class CL_VENDOR in a private internal table. I would like to be able to do this:

    codes = VALUE #( FOR vendor IN vendors ( vendor->code ) ).

    But until now, as far as I understand, if I want to iterate through all the vendors I am forced to expose the CL_VENDORS private internal table.

    Are there any plans to offer standardized iterators compatible with the FOR expression and can be implemented by any custom class?



      • Hi Sandra,

        Today I’m using an internal table to implement the list of vendors. But tomorrow I may need to implement it in a completely different way (a collection, Object Services, lazy instancing, etc.). Or I may need to change the Internal table’s structure by replacing the simple list of references to CL_VENDOR with a more elaborate line including an indexed ID together with the reference, for performance reasons.

        So, as I see it, exposing my data as a public read-only internal table is not a solution because it exposes and locks down the way the list is implemented. Simply put, it breaks encapsulation.

        In order to avoid this I am declaring the internal table as private and implementing a public method called GET_LIST() which returns a list of references to CL_VENDOR. With this approach at least, unlike the public read-only, I still have the chance to adapt the GET_LIST() method If I ever change the internal implementation of the list. So I guess we can argue that using a PRIVATE internal table together with a GET_LIST() preserves encapsulation.

        Still, having to use a GET_LIST() is not the most elegant approach. To cope with these scenarios most programming languages today directly support iteration of custom classes. That’s  why I was  curious to know if ABAP is planning to support these in the future.

        Note: You could argue that, if the implementation is changed, I could still find a way to keep that public read-only internal table in sync with the actual list of vendors. But that would mean adding unnecessary complexity to the class.

  • Hi,
    I want to ask you if there is a way to use this sentence, when a value depends on other value, that is calculated inside the same FOR.  For instance in code of attached image, the value of ATBEZ, depends on IDENT.
    Thank You!
    • I’ve just stumbled over the same requirement. I took the easy exit and extracted the calculations into a method, which makes the whole loop even more readable.

      But, I do have another unsolved problem: the WHERE condition can’t be checked against a function result.

      " have to use an additional variable
          DATA(wh_datub) = result-wa_db_datub + 365.
          result-it_da = VALUE tt_da(  FOR i = result-wa_db_datuv - 365 
                                       THEN i + 1
                                       WHILE i <= wh_datub 
                                       (  fetch_values_for_date( ia_datub = conv #( i ) i_fabkl = fabkl i_wa_db_datub = result-wa_db_datub i_wa_db_datuv = result-wa_db_datuv ) )
      " would expect that this is possible
          result-it_da = VALUE tt_da(  FOR i = result-wa_db_datuv - 365 
                                       THEN i + 1
                                       WHILE i <= result-wa_db_datub + 365 " <-- why isn't an expression possible?
                                       (  fetch_values_for_date( ia_datub = conv #( i ) i_fabkl = fabkl i_wa_db_datub = result-wa_db_datub i_wa_db_datuv = result-wa_db_datuv ) )