1

We all know these excellent ABAP statements which allows finding unique values in one-liner:

it_unique = VALUE #( FOR GROUPS value OF <line> IN it_itab 
                     GROUP BY <line>-field WITHOUT MEMBERS ( value ) ).

But what about extracting duplicates? Can one utilize GROUP BY syntax for that task or, maybe, table comprehensions are more useful here?

The only (though not very elegant) way I found is:

LOOP AT lt_marc ASSIGNING FIELD-SYMBOL(<fs_marc>) GROUP BY ( matnr = <fs_marc>-matnr 
                                                             werks = <fs_marc>-werks )
                                                  ASSIGNING FIELD-SYMBOL(<group>).
  members = VALUE #( FOR m IN GROUP <group> ( m ) ).

  IF lines( members ) > 1.
    "throw error
  ENDIF.

ENDLOOP.

Is there more beautiful way of finding duplicates by arbitrary key?

3 Answers 3

4

So, I just put it as answer, as we with Florian weren't able to think out something better.
If somebody is able to improve it, just do it.

TYPES tt_materials TYPE STANDARD TABLE OF marc WITH DEFAULT KEY. 

DATA duplicates TYPE tt_materials. 
LOOP AT materials INTO DATA(material) 
GROUP BY ( id = material-matnr 
           status = material-pstat 
           size = GROUP SIZE ) 
ASCENDING REFERENCE INTO DATA(group_ref). 

CHECK group_ref->*-size > 1. 
duplicates = VALUE tt_materials( BASE duplicates FOR <status> IN GROUP group_ref ( <status> ) ). 

ENDLOOP.
Sign up to request clarification or add additional context in comments.

Comments

2

Given

TYPES: BEGIN OF key_row_type,
         matnr TYPE matnr,
         werks TYPE werks_d,
       END OF key_row_type.
TYPES key_table_type TYPE
  STANDARD TABLE OF key_row_type
  WITH DEFAULT KEY.

TYPES: BEGIN OF group_row_type,
         matnr TYPE matnr,
         werks TYPE werks_d,
         size  TYPE i,
       END OF group_row_type.
TYPES group_table_type TYPE
  STANDARD TABLE OF group_row_type
  WITH DEFAULT KEY.

TYPES tt_materials TYPE STANDARD TABLE OF marc WITH DEFAULT KEY.
DATA(materials) = VALUE tt_materials(
  ( matnr = '23' werks = 'US' maabc = 'B' )
  ( matnr = '42' werks = 'DE' maabc = 'A' )
  ( matnr = '42' werks = 'DE' maabc = 'B' ) ).

When

DATA(duplicates) =
  VALUE key_table_type(
    FOR key IN VALUE group_table_type(
      FOR GROUPS group OF material IN materials
      GROUP BY ( matnr = material-matnr
                 werks = material-werks
                 size  = GROUP SIZE )
      WITHOUT MEMBERS ( group ) )
    WHERE ( size > 1 )
    ( matnr = key-matnr
      werks = key-werks ) ).

Then

cl_abap_unit_assert=>assert_equals(
    act = duplicates
    exp = VALUE tt_materials( ( matnr = '42' werks = 'DE') ) ).

Readability of this solution is so bad that you should only ever use it in a method with a revealing name like collect_duplicate_keys.

Also note that the statement's length increases with a growing number of key fields, as the GROUP SIZE addition requires listing the key fields one by one as a list of simple types.

8 Comments

whether you want the table of extracted duplicates to contain duplicates itself, or reduce them to unique keys, as the solution to the latter was given in my question, you could guess I want the former :) I want to extract duplicates to separate table without affecting the initial table
Your question actually only provides a solution to "how do I throw an error if my table contains duplicates".
check the first snippet with it_unique table, it does exactly your first use-case, which you already removed from the answer
Okay. Updated the answer.
Readability of this solution is so bad , yep, it's horrible :) Your first variant was much better. I made a solution based on it. Can we make it more concise? Can we exclude unique groups (size=1) some way except explicit checking inside loop?
|
0

What about the classics? I'm not sure if they are deprecated or so, but my first think is about to create a table clone, DELETE ADJACENT-DUPLICATES on it and then just compare both lines( )... I'll be eager to read new options.

9 Comments

Classics is not convenient as it requires deleting, so one need additional temp itab to accomplish the task. In case table consists of millions records it's not cool.
Ok, I see your point, and I really appreciate -and like- it. So, the answer will be the use of that new sentences I'm not familiar with. I'll keep my answer here instead of delete it to prevent further oldies to write the same answer ;)
Yep. The beauty of VALUE operator is that it constructs new table from initial and the initial table is untouched.
COLLECT is still a bad option? If you are creating a new useless table anyways, maybe you can COLLECT your "key" fields and a value of 1 into that new table and just raise your error when any row reaches value of 2... Again, I'm not sure if I'm wrong, but while we wait for a better answer, this conversation is useful (for me, at least ;) )
How do you raise error via COLLECT? In case of violation of primary key in a COLLECT structure it just results in a non-handleable exception (short-dump). Not cool :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.