2023 Apr 25 1:56 PM
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)
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; }
2023 Apr 26 10:18 AM
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; }
2023 Apr 25 2:48 PM
Why do you use "Unique key" if there are duplicates entries ?
2023 Apr 25 2:50 PM
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 ...
2023 Apr 25 4:22 PM
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.
2023 Apr 25 4:27 PM
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.
2023 Apr 25 4:37 PM
SELECT DISTINCT * FROM @your_itab as your_itab INTO TABLE @your_new_table.
should work... or should not?
2023 Apr 26 10:18 AM
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; }
2023 Apr 26 7:11 PM
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.