Application Development and Automation Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Create internal table with contructor expression for a complex scenario

miszabo
Discoverer
1,178

Hello ABAP Experts!
I'm looking for a solution to the following complex scenario (to deepen my understanding of constructor expressions and how they can be combined).

Task: Creation of an internal (sorted) table from another standard internal table (which contains duplicates)

  1. in one expression
  2. without loops
  3. existing data records are to be kept
  4. avoiding duplicate problems
  5. select records from source table by condition (WHERE)
  6. perform field mapping (fields have different names)
  7. perform value assignment for an extra field in destination table


I found two solutions, but each has a deficit
a) Either the duplicates are taken into account, but then the WHERE condition is not possible, or
b) the WHERE is possible, but then the problem of duplicates cannot be addressed.

Is there any solution at all?
Thank your for your help.

This is my test set-up:

 TYPES: ts_k1 TYPE n LENGTH 1,
        ts_k2 TYPE n LENGTH 1,
        ts_val TYPE c LENGTH 1, 
        ts_country TYPE c LENGTH 2. 

 TYPES: BEGIN OF ts_source,           
           k1      TYPE ts_k1, 
           k2      TYPE ts_k1,
           val     TYPE ts_val,     " field mapping 
           country TYPE ts_country, " Where condition 
       END OF ts_source, 
       tt_source TYPE STANDARD TABLE OF ts_source WITH NON-UNIQUE DEFAULT KEY. 

 TYPES: BEGIN OF ts_destination,
          k1    TYPE ts_k1, 
          k2    TYPE ts_k1,
          zzval TYPE ts_val, " field mapping 
          extra TYPE ts_val, " extra field 
       END OF ts_destination, 
       tt_destination TYPE SORTED TABLE OF ts_destination WITH UNIQUE KEY k1 k2.
 START-OF-SELECTION. 
*1| Create test data DATA(source_t) = VALUE tt_source( ( k1 = '1' k2 = '1' val = 'A' country = 'DE' )
( k1 = '1' k2 = '2' val = 'B' country = 'DE' ) ( k1 = '1' k2 = '3' val = 'C' country = 'EN' ) " <- exclude by WHERE condition ( k1 = '1' k2 = '4' val = 'D' country = 'DE' ) " <- dupplicate key ( k1 = '1' k2 = '4' val = 'D' country = 'DE' ) " <- dupplicate key ( k1 = '1' k2 = '5' val = 'E' country = 'DE' ) ). DATA(destination_t) = VALUE tt_destination( ( k1 = '1' k2 = '6' zzval = 'F' extra = '' ) ). *** solution a) addressing duplicates, but missing: WHERE country destination_t = CORRESPONDING #( BASE ( destination_t ) VALUE tt_destination( FOR wa IN CORRESPONDING tt_destination( source_t DISCARDING DUPLICATES MAPPING zzval = val ) " WHERE country .... not possible, because table already created! ( VALUE #( BASE CORRESPONDING #( wa ) extra = 'X' ) ) ) ). *** solution b) with WHERE condition (country), but missing: duplicates handling destination_t = CORRESPONDING tt_destination( VALUE tt_destination( FOR line IN source_t WHERE ( country = 'DE' ) ( CORRESPONDING #( BASE ( VALUE #( extra = 'X' ) ) line MAPPING zzval = val ) ) ) ). "DISCARDING DUPLICATES not possible by syntax checker, why?

SPAN { font-family: "Courier New"; font-size: 10pt; color: #000000; background: #FFFFFF; }.L0S31 { font-style: italic; color: #808080; }.L0S32 { color: #3399FF; }.L0S33 { color: #4DA619; }.L0S52 { color: #0000FF; }.L0S55 { color: #800080; }

1 ACCEPTED SOLUTION

miszabo
Discoverer
0 Kudos
1,050

Thank you @Sandra Rossi, great help.
With your suggestion as a basis, the solution covering all requirements would look like this:

    destination_t = VALUE #(                                             " one statement, no loop
                      LET aux_itab = VALUE tt_source( FOR wa2 IN source_t   " handle condition
                                         WHERE ( country = 'DE' )
                                         ( wa2 ) )
                      IN
                      FOR <line> IN CORRESPONDING tt_destination(
                                  aux_itab
                                  DISCARDING DUPLICATES                    " handle duplicates
                                  MAPPING zzval = val                        " handle mapping
                                  )
                                  ( VALUE #(
                                      BASE CORRESPONDING #( <line> )
                                      extra = 'X' ) ) ).                   " handle extra field


Any recommendations for improvements?
Or ist this the best we can currently achieve (with one statement and without loop)?

In contrast: The solution with loop would look like this:

LOOP AT source_t ASSIGNING FIELD-SYMBOL(<source_s>) WHERE country = 'DE'.   " <- handle condition
INSERT CORRESPONDING #( BASE ( VALUE #( extra = 'X' ) ) <source_s> MAPPING zzval = val ) INTO TABLE destination_t.
ENDLOOP.

SPAN { font-family: "Courier New"; font-size: 10pt; color: #000000; background: #FFFFFF; }.L0S31 { font-style: italic; color: #808080; }.L0S33 { color: #4DA619; }.L0S52 { color: #0000FF; }.L0S55 { color: #800080; }

7 REPLIES 7

FredericGirod
Active Contributor
1,050

Why do you use "Unique key" if there are duplicates entries ?

Sandra_Rossi
Active Contributor
1,050

Please edit your question, select your code and press the button [CODE], which makes the code appear colored/indented, it will be easier for people to look at it. Thank you!

Moreover, your post contains SPAN { font-family ...

miszabo
Discoverer
1,050

Thank you for your suggestions. I applied the code button. Should be reader friendly now.

frdric.girod :
Having duplicates in the source table (which must therefore be a standard table) and have them properly handled before the lines are copied into the target table is part of the scenario.

Sandra_Rossi
Active Contributor
0 Kudos
1,050

The syntax check complains with CORRESPONDING and DISCARDING DUPLICATES if the itab is built using a constructor expression or a functional method, it can work only on an internal table (tested in 7.52).

I can't find any source in the ABAP documentation to support this assumption.

To have DISCARDING DUPLICATES work with a constructor expression or a functional method, I had to use an ugly solution.

Simplifying your example:

CLASS lcl_app DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    TYPES: ts_k1      TYPE n LENGTH 1,
           ts_k2      TYPE n LENGTH 1,
           ts_val     TYPE c LENGTH 1,
           ts_country TYPE c LENGTH 2.

    TYPES: BEGIN OF ts_source,
             k1      TYPE ts_k1,
             k2      TYPE ts_k1,
             val     TYPE ts_val, " field mapping
             country TYPE ts_country, " Where condition
           END OF ts_source,
           tt_source TYPE STANDARD TABLE OF ts_source WITH NON-UNIQUE DEFAULT KEY.

    TYPES: BEGIN OF ts_destination,
             k1    TYPE ts_k1,
             k2    TYPE ts_k1,
             zzval TYPE ts_val, " field mapping
             extra TYPE ts_val, " extra field
           END OF ts_destination,
           tt_destination TYPE SORTED TABLE OF ts_destination WITH UNIQUE KEY k1 k2.
    METHODS without_discarding_duplicates FOR TESTING.
    METHODS with_discarding_duplicates FOR TESTING.
    METHODS setup.
    METHODS itab_method returning value(result) TYPE tt_source.
    DATA: source_t      TYPE tt_source,
          destination_t TYPE tt_destination.
ENDCLASS.

CLASS lcl_app IMPLEMENTATION.
  METHOD setup.
    source_t = VALUE #(
        ( k1 = '1' k2 = '1' val = 'A' country = 'DE' )
        ( k1 = '1' k2 = '2' val = 'B' country = 'DE' )
        ( k1 = '1' k2 = '3' val = 'C' country = 'EN' ) " <- exclude by WHERE condition
        ( k1 = '1' k2 = '4' val = 'D' country = 'DE' ) " <- dupplicate key
        ( k1 = '1' k2 = '4' val = 'D' country = 'DE' ) " <- dupplicate key
        ( k1 = '1' k2 = '5' val = 'E' country = 'DE' ) ).
  ENDMETHOD.
  METHOD without_discarding_duplicates.
    " RUNTIME ERROR ITAB_DUPLICATE_KEY AS EXPECTED - CAN'T BE CAUGHT BY ABAP UNIT
*    destination_t = CORRESPONDING tt_destination(
**                        itab_method( ) "<=== just to play a little bit
*                        VALUE tt_source( FOR wa2 IN source_t
*                                         WHERE ( country = 'DE' )
*                                         ( wa2 ) )
**                        DISCARDING DUPLICATES "<=== just to play a little bit
*                        ).
  ENDMETHOD.
  METHOD itab_method.
  ENDMETHOD.
  METHOD with_discarding_duplicates.
    destination_t = VALUE #(
                      LET aux_itab = VALUE tt_source( FOR wa2 IN source_t
                                         WHERE ( country = 'DE' )
                                         ( wa2 ) )
                      IN
                      FOR <line> IN CORRESPONDING tt_destination(
                                  aux_itab
                                  DISCARDING DUPLICATES
                                  )
                      ( <line> ) ).
    cl_abap_unit_assert=>assert_equals(
      act = destination_t
      exp = VALUE tt_destination(
            ( k1 = '1' k2 = '1' )
            ( k1 = '1' k2 = '2' )
            ( k1 = '1' k2 = '4' )
            ( k1 = '1' k2 = '5' ) ) ).
  ENDMETHOD.
ENDCLASS.

VXLozano
Active Contributor
0 Kudos
1,050
SELECT DISTINCT * FROM  @your_itab as your_itab INTO TABLE  @your_new_table.

should work... or should not?

miszabo
Discoverer
0 Kudos
1,051

Thank you @Sandra Rossi, great help.
With your suggestion as a basis, the solution covering all requirements would look like this:

    destination_t = VALUE #(                                             " one statement, no loop
                      LET aux_itab = VALUE tt_source( FOR wa2 IN source_t   " handle condition
                                         WHERE ( country = 'DE' )
                                         ( wa2 ) )
                      IN
                      FOR <line> IN CORRESPONDING tt_destination(
                                  aux_itab
                                  DISCARDING DUPLICATES                    " handle duplicates
                                  MAPPING zzval = val                        " handle mapping
                                  )
                                  ( VALUE #(
                                      BASE CORRESPONDING #( <line> )
                                      extra = 'X' ) ) ).                   " handle extra field


Any recommendations for improvements?
Or ist this the best we can currently achieve (with one statement and without loop)?

In contrast: The solution with loop would look like this:

LOOP AT source_t ASSIGNING FIELD-SYMBOL(<source_s>) WHERE country = 'DE'.   " <- handle condition
INSERT CORRESPONDING #( BASE ( VALUE #( extra = 'X' ) ) <source_s> MAPPING zzval = val ) INTO TABLE destination_t.
ENDLOOP.

SPAN { font-family: "Courier New"; font-size: 10pt; color: #000000; background: #FFFFFF; }.L0S31 { font-style: italic; color: #808080; }.L0S33 { color: #4DA619; }.L0S52 { color: #0000FF; }.L0S55 { color: #800080; }

1,050

Of course I would choose the LOOP AT solution, just because it's easy to understand and maintainable.

Thinking that constructor expressions are faster than the old ABAP is a myth. Constructor expressions are to be used to make the code more understandable and maintainable, if it ends in code too much complex, forget it.