Skip to Content
Author's profile photo Horst Keller

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

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 …

Assigned Tags

      77 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Christian Guenter
      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

        A bug ...

      Author's profile photo Christian Guenter
      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

      Author's profile photo Christian Guenter
      Christian Guenter

      I just recognized that also a dump was created

      2015-04-15 17_44_17-Runtime Error Long Text.png

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Another one bytes the dust ...

       

      And we really thought, that we have tested thoroughly enough

       

      I'm curious how many you will still find of those ....

      Author's profile photo Christian Guenter
      Christian Guenter

      i'll give my best

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Patches for both errors are initiated now.

      Karsten Bohlmann invites you to a coffee at next TechEd

      Author's profile photo Attila Laczko
      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      The termination condition offs <= strlen( l_item ) is met from the beginning isn't it?

      Author's profile photo Attila Laczko
      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      how can I define the literal "space" in string pattern

      Why not simply ` `? Or `\s`?

       

      See Special Characters in Regular Expressions

      Author's profile photo Attila Laczko
      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog 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 ....

      Author's profile photo Nishant Bansal
      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

      Author's profile photo Timothy Muchena
      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Combining table expressions with appropriate constructor expressions should do.

      Author's profile photo Michael Fritz
      Michael Fritz

      Horst,some days ago I've posted a question on SCN but did not get an answer so far.Perhaps you can jump in with a hint. Please see my OP at:

      http://scn.sap.com/thread/3797343

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Back from vacation and

      Christian Guenter  solved it already, very good

      Author's profile photo Arshad Ansary
      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               

      Author's profile photo Attila Laczko
      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Yep

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog 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( sy-uzeit )

         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

      Author's profile photo Michael Fritz
      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog 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

      Author's profile photo Attila Laczko
      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

      Author's profile photo Former Member
      Former Member

      Hi Attila,

       

      See the response from Jacques Nomssi here - How much ABAP740 (read: functional ABAP programming) is too much?.

       

      BR,

      Suhas

      Author's profile photo Jens Schwendemann
      Jens Schwendemann

      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.
        
        EXIT
      ENDLOOP.

       

      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

       

      Chees

      Jens

      Author's profile photo Sandra Rossi
      Sandra Rossi

      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.

      Author's profile photo Jens Schwendemann
      Jens Schwendemann

      True, fixed by disclaimer

      Author's profile photo Christian Guenter
      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( ).

      Author's profile photo Former Member
      Former Member

      What happened to KISS principle?

       

      I am continuing the discussion here - How much ABAP740 (read: functional ABAP programming) is too much?.

      Author's profile photo Michael Fritz
      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.

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Hi Alexander,

       

      Ask me anything about ABAP language, but please not about upgrades

       

      Horst

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog 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.

      Author's profile photo Łukasz Pęgiel
      Łukasz Pęgiel

      It's already there, please check Customize Syntax Coloring in ABAP in Eclipse. Maybe not all is yet possible, but enough.

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Niall Brennan
      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 + wa-amount + wa-sales_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 + wa-amount + wa-sales_tax_amount

                                      ).

       

      Thanks Horst and Suhas.

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog 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.

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Hmm, by using a c field as "correct" type …

      Not so good.

      Author's profile photo Former Member
      Former Member

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

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Duh, the documentation says how the type of the helper variable is derived.

      Author's profile photo Former Member
      Former Member

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

      Author's profile photo Horst Keller
      Horst Keller
      Blog 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.

      Author's profile photo Former Member
      Former Member

      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.

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      If you type the first INIT variable, you can always use # for the general result.

      Author's profile photo Former Member
      Former Member

      Is there a reason why this is not implemented as default behaviour?

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      Cause it is a constructor expression and follows the general rules of those.

      Author's profile photo Rakshith Gore
      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

       

      Author's profile photo Horst Keller
      Horst Keller
      Blog 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

       

      Author's profile photo Rakshith Gore
      Rakshith Gore

      Thanks Horst 🙂

       

      Author's profile photo David Kunz
      David Kunz

      Hello Horst,

       

      I'm wondering... you have filter, reduce, ... but what about map?

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

      See corresponding.

      Author's profile photo David Kunz
      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.

      Author's profile photo Horst Keller
      Horst Keller
      Blog 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.

       

       

       

      Author's profile photo umashankar dasari
      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.

      Author's profile photo Indranil Dutt
      Indranil Dutt

      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.

       

      Author's profile photo Horst Keller
      Horst Keller
      Blog Post Author

       

      https://help.sap.com/http.svc/rc/abapdocu_752_index_htm/7.52/en-US/index.htm?file=abenconstructor_expr_corresponding.htm

      Author's profile photo Michael Umlauff
      Michael Umlauff

       

      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.

      ENDLOOP.

      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

      Michael

      Author's profile photo Michael Umlauff
      Michael Umlauff

      In my previous post all the code indentations were lost, so I try it with some screenshots:

      Author's profile photo K Shiva
      K Shiva

      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

       

      Regards,

      Shiva

      Author's profile photo Nuno Godinho
      Nuno Godinho

      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?

       

      Thanks,
      Nuno

      Author's profile photo Sandra Rossi
      Sandra Rossi

      (maybe you can also explain why a public read-only internal table is not a solution)

      Author's profile photo Sergio FRAGA
      Sergio FRAGA

      To follow OO best practice of encapsulation??

      Author's profile photo Sandra Rossi
      Sandra Rossi

      No. Thx for the tip 😉

      Author's profile photo Sergio FRAGA
      Sergio FRAGA

      “public read-only attributes” it a special caveat of ABAP, but yes, it respects encapsulation. So yes, it's not due to encapsulation..

      Author's profile photo Nuno Godinho
      Nuno Godinho

      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.

      Author's profile photo Sandra Rossi
      Sandra Rossi

      It reminds me this blog post about CL_OBJECT_COLLECTION.

      Author's profile photo Nuno Godinho
      Nuno Godinho

      Hi Sandra, thank you for sharing that blog post. I hadn’t read it. They are indeed related.

      You didn’t mention if I managed to make it clear why using a public read-only internal table breaks encapsulation.

      Author's profile photo Sandra Rossi
      Sandra Rossi

      I can only say that the topic is beyond my competence.

      Author's profile photo David de la Rosa
      David de la Rosa
      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!
      Example
      Author's profile photo Dominik Augustin
      Dominik Augustin

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