Skip to Content
Author's profile photo Horst Keller

I Don’t Like REDUCE, I Love It

Recently I stumbled over the examples in the documentation of built-in functions ROUND and RESCALE.

(Does anybody use RESCALE?)

There are tables with results of these functions for different values of the arguments, but there was no coding example how to achieve these results. I guess,  the functions were called  with different arguments one by one and the results were copied into the documentation one by one.

But, hey, we have other possibilities in ABAP now and I’ve tried to recreate the results with CL_DEMO_OUTPUT and REDUCE (as a recreational measure so to say).

I cannot refrain from showing you that (a bit of showing off):

TYPES:
   BEGIN OF line,
     arg       TYPE i,
     result    TYPE decfloat34,
     scale     TYPE i,
     precision TYPE i,
   END OF line,
   result TYPE STANDARD TABLE OF line WITH EMPTY KEY.

DATA(val) = CONV decfloat34( ‘1234.56789 ‘ ).
DATA(out) = cl_demo_output=>new(
   )->begin_section( ‘Value’
   )->write(
     |{ val
      }, scale = { cl_abap_math=>get_scale( val )
      }, precision = { cl_abap_math=>get_number_of_digits( val ) }|
   )->begin_section( ‘Round with dec’
   )->write(
    REDUCE result(
      INIT tab TYPE result
      FOR i = 5 UNTIL i > 6
      LET rddec = round( val = val dec = i
          mode  = cl_abap_math=>round_half_up ) IN
      NEXT tab = VALUE #( BASE tab
       ( arg = i
         result = rddec
         scale = cl_abap_math=>get_scale( rddec )
         precision = cl_abap_math=>get_number_of_digits( rddec )
       ) ) )
   )->next_section( ‘Round with prec’
   )->write(
    REDUCE result(
      INIT tab TYPE result
      FOR i = UNTIL i > 10
      LET rdprec = round( val = val prec = i
          mode   = cl_abap_math=>round_half_up ) IN
      NEXT tab = VALUE #( BASE tab
       ( arg = i
         result = rdprec
         scale = cl_abap_math=>get_scale( rdprec )
         precision = cl_abap_math=>get_number_of_digits( rdprec )
       ) ) )
   )->next_section( ‘Rescale with dec’
   )->write(
    REDUCE result(
      INIT tab TYPE result
      FOR i = 5 UNTIL i > 8
      LET rsdec = rescale( val = val dec = i
          mode  = cl_abap_math=>round_half_up ) IN
      NEXT tab  = VALUE #( BASE tab
       ( arg = i
         result = rsdec
         scale = cl_abap_math=>get_scale( rsdec )
         precision = cl_abap_math=>get_number_of_digits( rsdec )
       ) ) )
   )->next_section( ‘Rescale with prec’
   )->write(
    REDUCE result(
      INIT tab TYPE result
      FOR i = UNTIL i > 12
      LET rsprec = rescale( val = val prec = i
          mode   = cl_abap_math=>round_half_up ) IN
      NEXT tab = VALUE #( BASE tab
       ( arg = i
         result = rsprec
         scale = cl_abap_math=>get_scale( rsprec )
         precision = cl_abap_math=>get_number_of_digits( rsprec )
       ) ) )
   )->display( ).

Giving

/wp-content/uploads/2016/08/reduce_1023009.jpg

Isn’t REDUCE just awesome? For me, one of the most powerful of all the constructor operators. Use it to get used to it. Believe me, after some training, you even don’t have to look up its documentation any more.

Assigned tags

      13 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Isn't REDUCE just awesome? For me, one of the most powerful of all the constructor operators. Use it to get used to it.

      I use the REDUCE (aka table-reduction) operator often in productive code. Imo, it makes the code so much cleaner 😘

      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      Does anybody use RESCALE?

      Arrgh, stop rubbing it in, Horst! Our system is too old even for the "Next generation ABAP development". 🙁

      I do appreciate your samples though, thank you!

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

      RESCALE was 7.02, but I never used it 😉

      Author's profile photo Sandra Rossi
      Sandra Rossi

      Horst, in these examples, is there a reason to use REDUCE instead of VALUE? Thx.

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

      Yep, the INIT.

      Author's profile photo Sandra Rossi
      Sandra Rossi

      Thx. But I didn't explain well my question. I couldn't test it until today, but the first REDUCE is equivalent to this VALUE expression:

          VALUE result(
            FOR i = -5 UNTIL i > 6
            LET rddec = round( val = val dec = i
                mode  = cl_abap_math=>round_half_up ) IN
            ( VALUE #(
               arg = i
               result = rddec
               scale = cl_abap_math=>get_scale( rddec )
               precision = cl_abap_math=>get_number_of_digits( rddec )
             ) ) )
      

      So, is there a reason to use REDUCE instead of VALUE? 😉

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

      In fact no, you can replace all the REDUCEs with your VALUEs in this case 😡 .

      Let's say it is a question of taste. When I have such a task, I start with REDUCE, because I LOVE it 😛

      PS: Sandra, your'e simply the best ...

      Author's profile photo Sandra Rossi
      Sandra Rossi

      Thx Horst for the clarification 😉 (PS: sorry but honestly you're far ahead, and I'm sure nobody will contradict me!)

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

      Remember, it's still your JS code that synchronizes the tree of the Portal Version of the ABAP docu 😉

      Author's profile photo Sandra Rossi
      Sandra Rossi

      LOL

      Author's profile photo Stefan Riedel-Seifert
      Stefan Riedel-Seifert

      Very cool, but it seems, that i cannot solve the following problem with the statement reduce:

      assuming having an internal table with the follwing entries:

      KEY	BUSINESS_AREA	PROCESS_COMMUNICATION
      1	1		1
      1	1		2
      1	2		2
      1	3		1

      The content should be used in a table grid, but only one line per object

      KEY 	BUSINESS_AREA 	PROCESS_COMMUNICATION
      1 	1,2,3		1,2

      i.e. i have to reduce the content by the key like a projection. The separation of the content should be flexible: , or '<br/>', ...

      I played around with the reduce-statement, but could not solve this and ended up something like (control level processing):

      loop at it_data into data(wa_data).
        at new key.
          ...
        endat.
        at new business_area.
          ...
        endat.
        ...
      endloop.

       

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

       

      TYPES:
        BEGIN OF line,
          key  TYPE i,
          col1 TYPE string,
          col2 TYPE string,
        END OF line,
        itab TYPE TABLE OF line WITH EMPTY KEY.
      
      DATA(itab) = VALUE itab(
        ( key = 1 col1 = `1` col2 = `1` )
        ( key = 1 col1 = `1` col2 = `2` )
        ( key = 1 col1 = `2` col2 = `2` )
        ( key = 1 col1 = `3` col2 = `1` ) ).
      
      DATA(result) = REDUCE line(
        INIT r TYPE line
        FOR GROUPS OF wa IN itab GROUP BY wa-key
        FOR mem IN GROUP wa
        NEXT r = VALUE #( LET buff = r IN
                          key = mem-key
                          col1 = buff-col1 && cond #( when buff-col1 ns mem-col1 then mem-col1 )
                          col2 = buff-col2 && cond #( when buff-col2 ns mem-col2 then mem-col2 ) ) ).
      ​
      Author's profile photo Stefan Riedel-Seifert
      Stefan Riedel-Seifert

      That’s a very good starting point. Assume having this internal table

      DATA(itab) = VALUE itab( ( key = 1 col1 = `1` col2 = `1` )
                               ( key = 1 col1 = `3` col2 = `2` )
                               ( key = 1 col1 = `2` col2 = `2` )
                               ( key = 1 col1 = `3` col2 = `1` )
                               ( key = 2 col1 = `4` col2 = `1` )
                               ( key = 2 col1 = `4` col2 = `2` )
                               ( key = 2 col1 = `5` col2 = `5` )
                               ( key = 2 col1 = `8` col2 = `1` ) ).

      and the goal is this output

      KEY COL1 COL2
      1 1,2,3 1,2
      2 4,5,8 1,2,5

       

      I slightly changed your code to

       

      SORT itab BY col1 col2.
      
      DATA(results) =
        REDUCE itab(
          INIT tab TYPE result
          FOR GROUPS OF wa IN itab GROUP BY wa-key
      
          NEXT tab = VALUE #(
          BASE tab
           (
            REDUCE line(
            INIT r TYPE line
            FOR mem IN GROUP wa
      
            NEXT r = VALUE #( LET buff = r IN
                              key  = mem-key
      
            col1 = COND #( WHEN buff-col1 IS INITIAL THEN buff-col1 && COND #( WHEN buff-col1 NS mem-col1 THEN        mem-col1 )
                           ELSE buff-col1                           && COND #( WHEN buff-col1 NS mem-col1 THEN ',' && mem-col1 ) )
      
            col2 = COND #( WHEN buff-col2 IS INITIAL THEN buff-col2 && COND #( WHEN buff-col2 NS mem-col2 THEN        mem-col2 )
                           ELSE buff-col2                           && COND #( WHEN buff-col2 NS mem-col2 THEN ',' && mem-col2 ) )
          ) )  ) ) ).
      
      cl_demo_output=>display( results ).
      

      to achieve this. Maybe, that this coding can be condensed :-).

      By the way: some trivial performance considerations:

      1) My first old fashioned try with at. … endat. constructions cost. approx 23.000 microseconds

      2) The new one more than twice (55.000 microseconds)

      My initial table has ~ 4.000 entries and 7 columns. The condensed result ~3.000 entries. 

      3 columns has no need to be condensed and i can derive the content from the mem-structure.
      Doing so, the runtime shrinks (45.000 microseconds).

      When playing with the ‘new statements’ i often had the experience, that the codings looks cool, but at the expensive of performance.

       

      Beneath the performance considerations, the pretty printer is not able to format such coding anymore :-(.