"Cultural Appropriation" being the new SJW buzz word in America, I am doing in the public some "New ABAP syntax features appropriation" to get accustomed to them. I didn't find yet a really satisfyingly simple expression for the simple task
"Given an internal table, create an internal table containing all the distinct values that appear in a certain column"
So this really is something very simple, similar to a SELECT DISTINCT in SQL.
In a good programming language, a simple task must have a simple solution.
So let's try.
I select some arbitrary 100 rows from a database table VBAK (sales order headers) into a standard table of line type VBAK. Now I want to have a standard table of elementary line type ERNAM, containinig the distinct values for ERNAM that appear in my selection. (See my pastebin for the SELECT statement and the frame around the following code snippets).
Solution with COLLECT
Using the idiom COLLECT for standard tables (which is not encouraged by the documentation, but works fine), I get this readable solution:
form with_collect using it_vbak type vbak_tab changing ct_ernam type ernam_tab. clear ct_ernam. loop at it_vbak assigning field-symbol(<ls_vbak>). collect <ls_vbak>-ernam into ct_ernam. endloop. endform.
COLLECT uses an internal auxiliary hash table for achieving uniqueness.
Using an auxiliary hash table explicitly
The hash can be made explicit, then I have the following solution:
form with_explicit_aux_hash using it_vbak type vbak_tab changing ct_ernam type ernam_tab. data: lt_ernam type hashed table of ernam with unique key table_line. loop at it_vbak assigning field-symbol(<ls_vbak>). insert <ls_vbak>-ernam into table lt_ernam. endloop. ct_ernam = lt_ernam. endform.
Now for the new ABAP syntax idioms (not so new any more, by the way).
Using the VALUE operator
I found a solution with the VALUE operator:
form with_value_for using it_vbak type vbak_tab changing ct_ernam type ernam_tab. ct_ernam = value #( for <ls_vbak> in it_vbak ( <ls_vbak>-ernam ) " Inserting into a hash table will dump on multiple values. " No way to avoid this ). sort ct_ernam. delete adjacent duplicates from ct_ernam. endform.
Which is readable, but it's not nice that the implicit insert of table line ( <ls_vbak>-ernam ) throws an exception if the target table has a unique key. This can't be trapped inside of the expression. So we can't use an auxiliary hash table, like in the last example. We have to collect all the ERNAM's firstly, and then have to delete the duplicates later on.
Not nice. But I didn't see a better way to do it with VALUE. Do you?
Using the REDUCE operator
Just for curiosity, I also tried the REDUCE operator. I found a combination of REDUCE and COND to give the desired result. But the final expression is far too complicated for such a simple task:
form with_reduce_for using it_vbak type vbak_tab changing ct_ernam type ernam_tab. ct_ernam = reduce #( init names type ernam_tab for <ls_vbak> in it_vbak next names = cond #( when line_exists( names[ table_line = <ls_vbak>-ernam ] ) then names else value #( base names ( <ls_vbak>-ernam ) ) ) ). endform.
Using the GROUP BY keyword
But even with the GROUP BY idiom, which seems to be tailored for situations like the given, the solution is too long for my taste:
form with_grouping using it_vbak type vbak_tab changing ct_ernam type ernam_tab. clear ct_ernam. loop at it_vbak assigning field-symbol(<ls_vbak>) group by <ls_vbak>-ernam ascending assigning field-symbol(<lv_group>). append <lv_group> to ct_ernam. endloop. endform.
Other suggestions?
Find my complete example report ZZ_EXTRACT_SUBTABLE on pastebin
Maybe I am blind for quicker and better solutions! Any suggestions are welcome!