Skip to Content

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.:

TYPES:
  BEGIN OF line,
    col1 TYPE i,
    col2 TYPE i,
    col3 TYPE i,
  END OF line,
  itab TYPE STANDARD TABLE OF line WITH EMPTY KEY.

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

gives

COL1 COL2 COL3
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:

DATA itab TYPE STANDARD TABLE OF i WITH EMPTY KEY.
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 …

To report this post you need to login first.

58 Comments

You must be Logged on to comment or reply to a post.

  1. Christian Guenter

    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.

     

    Christian

    (0) 
  2. Christian Guenter

    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

    (0) 
  3. Attila Laczko

    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

    (0) 
      1. Attila Laczko

        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

        (0) 
          1. Attila Laczko

            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.

             

            Attila

            (0) 
            1. Horst Keller Post author

              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 ….

              (0) 
  4. Nishant Bansal

    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.??

     

    Thanks

    Nishant

    (0) 
  5. Timothy Muchena

    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

    (0) 
  6. Arshad Ansary

    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 ) ).        


    Regards

    Arshad               

    (0) 
    1. Attila Laczko

      Hi Arshad,

       

      I think you miss in this case the work area for the value transfer. It would be looking something like that:

       

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

       

      pretty simple

       

      Cheers

      Attila

      (0) 
  7. Suhas Saha

    Hello Horst,

     

    How does one EXIT a FOR iteration?

     

    E.g.,

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

                     WHERE FLDATE <= sy-datum.

      ” do something

      EXIT.

    ENDLOOP.

    How to build the FOR statement for these lines?

     

    BR,

    Suhas

    (0) 
    1. Horst Keller Post author

      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 ).

       

      TYPES itab TYPE SORTED TABLE OF i

                  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.

      ENDCLASS.

      TRY.

           DATA(jtab) = VALUE itab(

             FOR wa IN itab

             ( COND i( WHEN wa < 50 THEN wa

                        ELSE THROW my_exc( ) ) ) ).

         CATCH my_exc.

      ENDTRY.

       

      cl_demo_output=>display( jtab ).



      Best

      Horst

      (0) 
      1. Michael Fritz

        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.

         

        Michael

        (0) 
        1. Horst Keller Post author

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

           

          LOOP … WHERE …

            EXIT.

          ENDLOOP.

          IF sy-subrc = 0.

            …

          ENDIF.

           

          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.

           

          Horst

          (0) 
          1. Attila Laczko

            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,

            Attila

            (0) 
    2. Christian Guenter

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

       

       

       

      REPORT z_reduce_for_fun.

      CLASS lcl_reduce DEFINITION CREATE PUBLIC.

        PUBLIC SECTION.

          METHODS: start.

        PRIVATE SECTION.

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

      ENDCLASS.

      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 ).

        ENDMETHOD.

        METHOD do_something.

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

        ENDMETHOD.

      ENDCLASS.

      START-OF-SELECTION.
        NEW lcl_reduce( )->start( ).

      (0) 
  8. Michael Fritz

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

     

    /wp-content/uploads/2016/04/2016_04_12_135955_927978.png

    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?

     

    Thanks,

    Michael

     

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

    (0) 
    1. Suhas Saha

      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.

       

      BR,

      Suhas

       

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

      (0) 
  9. Alexander Petrenz

    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

    Alexander

    (0) 
      1. Alexander Petrenz

        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.

         

        Alexander

        (0) 
        1. Horst Keller Post author

          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.

          (0) 
            1. Alexander Petrenz

              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.

               

              Cheers

              (0) 
  10. Niall Brennan

    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.


    SOLUTION

    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.

    (0) 
    1. Suhas Saha

      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

       

      BR,

      Suhas

      (0) 
    2. Horst Keller Post author

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

      (0) 
      1. Christoph Pflügler

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

        This coding behaves erronously (no decimal places):

         

        DATA(lv_result=
             REDUCE decfloat16(
                     INIT 0
                     FOR <lv_float> IN lt_floats
                     NEXT + <lv_float>
             ).

         

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

         

        DATA(lv_result=
             REDUCE decfloat16(
                     INIT = 0.0′
                     FOR <lv_float> IN lt_floats
                     NEXT + <lv_float>
             ).

         

         

        Best regards,

        Christoph

        (0) 
          1. Christoph Pflügler

            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).

            (0) 
              1. Christoph Pflügler

                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).

                (0) 
                1. Horst Keller Post author

                  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.

                  (0) 
                  1. Christoph Pflügler

                    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.

                    (0) 
  11. Rakshith Gore

    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

     

    (0) 
  12. Horst Keller Post author

    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.

    Best

    Horst

     

    (0) 
      1. David Kunz

        I meant the ‘map’ functionality in other programming languages (e.g. JavaScript).

        Something like

        loop at itab assigning field-symbol(<wa>).

        function(<wa>).

        endloop.

        “for <wa> in itab  function(<wa>)”

         

         

        See e.g. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map

         

        … or a more general way like in python 

        https://www.tutorialspoint.com/python/python_for_loop.htm

         

        Map would come very handy if you guys implement lambda expressions.

        (0) 
        1. Horst Keller Post author

          That would be a FOR outside a constructor expression.

          So to say an extension of

          meth( <wa> ).

          to

          for <wa> in itab  meth(<wa>).

          In the moment the workaround would be a REDUCE with a dummy result. But that works only, if meth has a returning parameter. You want to call arbitrary methods/functions in a loop.

          The point is, I dont’ see the benefit of the short form, if you don’t use it at an operand position.

           

           

           

          (1) 
  13. umashankar dasari

    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.

    (0) 

Leave a Reply