2020 Mar 17 1:47 PM
This is the voting thread for the March 2020 SAP Community Coding Challenge. For the challenge details and directions see this blog:
https://blogs.sap.com/2020/02/28/sap-community-coding-challenge-series/
In this Question thread I will post the 7 finalist. Use the answer voting mechanism to choose the one solution you believe should be the overall winner. Remember: this is all for fun and education. We are all winners here because of the great knowledge sharing!
2020 Mar 17 1:49 PM
Finalist #1: CL_ABAP_MATCHER Approach
By pawelgrzeskowiak
SPLIT condense( sentence ) AT space INTO TABLE DATA(words).
out->write( |Number of words: { lines( words ) } | ).
LOOP AT words ASSIGNING FIELD-SYMBOL(<word>).
DATA(matcher) = NEW cl_abap_matcher(
regex = NEW cl_abap_regex( pattern = '(.)(?!.*\1)' )
text = <word> ).
out->write( |Number of unique characters in the word: { <word> } - { lines( matcher->find_all( ) ) } | ).
ENDLOOP.
2020 Mar 21 8:05 AM
I see that there are two times more people who prefer Finalist #1 over Finalist #2. Their solutions are almost identical except the way they invoke the regular expression and get the results:
I'm interested to hear from people the reasons why they prefer Finalist #1 over Finalist #2...
2020 Mar 29 10:59 PM
I would say Finalist #1 code looks more clean, in terms of readability, compared to Finalist #2.
2020 Mar 30 3:59 PM
I would go with this one as the easiest to read.
And it also follows the basis of clean code convention.
2020 Apr 11 12:29 PM
2020 Mar 17 1:51 PM
Finalist #2: Regex Approach
By: alexander_frank
SPLIT condense( sentence ) AT space INTO TABLE DATA(words).
out->write( |Number of words: { lines( words ) }| ).
LOOP AT words REFERENCE INTO DATA(word).
DATA(unique_chars_in_word) = replace( val = word->* regex = `(.)(?=.*\1)` with = `` occ = 0 ).
out->write( |Number of unique characters in the word: { word->* } - { strlen( unique_chars_in_word ) }| ).
ENDLOOP.
2020 Mar 17 2:58 PM
Although the regex looks weird and would be difficult to maintain by "standard" developers if there's an error or change request, the code is extremely clear. Bravo!
2020 Mar 17 3:06 PM
I like it, very clean code! But clear, the regex some time can be a pain to understand and if you don't have some nice variable naming you can end up scratching your head trying to understand what happens.
2020 Mar 18 12:28 AM
Agreed. Simple and clean.
I liked the other finalists as well. Neat to see all the difference approaches.
2020 Mar 18 5:06 AM
II like this solution. Well besides the variable name
2020 Mar 18 8:30 PM
2020 Mar 18 10:28 PM
Wow, this was more praise than I've gotten since a long time for my code. Perhaps I should also compliment other code more often.
My thought process was quite short, so I don't think it warrants a full blog post.
I tried to find the solution with the least lines of code that mostly complies with the SAP ABAP Style Guide. Since I've done Advent of Code and other coding challenges in ABAP, I strongly expected to use regex quite a lot if I wanted a short solution. Therefore this was my first implementation and I couldn't think about another solution afterwards that could possibly be tuned to less lines of code. Using a lot of functions like lines( ) or strlen( ) within string templates was needed in order to keep the lines of code low. Other choices like using REFERENCE INTO or putting the result of the replace( ) function with the regex into a distinct variable were done due to the Style Guide or Clean Code in general. I didn't assemble the regex with constants as it is proposed in the Style Guide since I thought that it wasn't too complex. But I'm sure this is debatable.
With what I know now, I would probably use count( ) instead of replace( ) since I then don't need the strlen( ) call and perhaps tweak a solution with REDUCE instead of LOOP AT. This blog post and its comments has some nice ideas: https://blogs.sap.com/2020/03/18/sap-community-coding-challenge-march-2020-my-approach/
2020 Mar 20 9:39 AM
Well done but constructing the correct Regex is another story...
2020 Mar 17 1:52 PM
Finalist #3: For/Group By Approach
By: sougata.chatterjee
SPLIT condense( sentence ) AT space INTO TABLE DATA(lt_words).
out->write( |Number of Words: { lines( lt_words ) }| ).
LOOP AT lt_words ASSIGNING FIELD-SYMBOL(<word>).
out->write(
|Number of unique characters in the word: { <word> } - {
lines( VALUE string_table(
FOR GROUPS x OF y
IN VALUE string_table(
FOR i = 0 THEN i + 1
UNTIL i = strlen( <word> ) ( <word>+i(1) ) )
GROUP BY y ( x ) ) ) }| ).
ENDLOOP.
2020 Mar 17 1:53 PM
Finalist #4: SELECT DISTINCT Approach
By: dominik.bigl2
SPLIT condense( sentence ) AT | | INTO TABLE DATA(words).
out->write( |Number of words: { lines( words ) } | ).
LOOP AT words ASSIGNING FIELD-SYMBOL(<word>).
DATA(characters) = VALUE ABAP_SORTORDER_tab( FOR char = 0 THEN char + 1 UNTIL char = strlen( <word> ) ( name = <word>+char(1) ) ).
SELECT DISTINCT * FROM @characters AS characters INTO TABLE @DATA(unique_characters).
out->write( |Number of unique characters in the word: { <word> } - { lines( unique_characters ) } | ).
ENDLOOP.
2020 Mar 17 2:12 PM
2020 Mar 17 2:33 PM
2020 Mar 17 2:42 PM
2020 Mar 17 2:47 PM
2020 Mar 17 2:52 PM
2020 Mar 17 2:52 PM
2020 Mar 17 2:56 PM
I mean, in Open SQL, SELECT ... FROM @ is only supported if the database is HANA (*). For instance, SAP Sybase ASE (16.0) doesn't support it (Developer Edition 7.52). Of course, it works with the ABAP Cloud environment, but generally speaking, many SAP clients don't have HANA (yet).
(*) EDIT:
SELECT DISTINCT works only if the database is HANA.
Complete explanation: FROM @ITAB works from 7.52 only if
2020 Mar 17 4:39 PM
2020 Mar 17 4:47 PM
Amazing, super clean, concise, readable and maintanable 😛 What more can you ask?
2020 Mar 17 5:09 PM
Beautiful than i wrote lol ! I should learn from them...Good job!
2020 Mar 17 7:23 PM
Very clever solution! Nothing I would implement in my code base (data transfer overhead for limited benefit) but extra kudo for creativity!
2020 Mar 18 5:04 AM
Selection from the internal table limits the application of this solution. Easier DELETE ADJACENT DUPLICATE ENTRIES FROM characters.
2020 Mar 18 7:19 AM
Fun solution but EXTREMELY slow, 700 times slower than Finalist #2 ! I thought the solution had to be realistic but in fact the probable winner will be the craziest. ¯\_(ツ)_/¯
CLASS ltc_main DEFINITION
FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA sentence TYPE string VALUE `ABАP is excellent `.
METHODS select_from_itab FOR TESTING.
METHODS regex FOR TESTING.
ENDCLASS.
CLASS ltc_main IMPLEMENTATION.
METHOD regex.
DO 3000 TIMES.
SPLIT condense( sentence ) AT space INTO TABLE DATA(words).
DATA(a) = |Number of words: { lines( words ) }|.
LOOP AT words REFERENCE INTO DATA(word).
DATA(unique_chars_in_word) = replace( val = word->* regex = `(.)(?=.*\1)` with = `` occ = 0 ).
DATA(b) = |Number of unique characters in the word: { word->* } - { strlen( unique_chars_in_word ) }|.
ENDLOOP.
ENDDO.
ENDMETHOD.
METHOD select_from_itab.
DO 3000 TIMES.
SPLIT condense( sentence ) AT | | INTO TABLE DATA(words).
DATA(a) = |Number of words: { lines( words ) } |.
LOOP AT words ASSIGNING FIELD-SYMBOL(<word>).
DATA(characters) = VALUE abap_sortorder_tab( FOR char = 0 THEN char + 1 UNTIL char = strlen( <word> ) ( name = <word>+char(1) ) ).
SELECT DISTINCT * FROM @characters AS characters INTO TABLE @DATA(unique_characters).
DATA(b) = |Number of unique characters in the word: { <word> } - { lines( unique_characters ) } |.
ENDLOOP.
ENDDO.
ENDMETHOD.
ENDCLASS.
2020 Mar 20 9:07 AM
Unfortunately, I didn't have this brilliant idea. I am a big fan of clean code. In terms of readability or maintainability, this is the cleanest solution in my opinion. Thank you Domi!
2020 Mar 20 9:41 AM
You should win ... if you don't you still should be given a gold medal for creativity! Well done!!!
2020 Mar 20 9:35 PM
2020 Mar 21 7:47 AM
I have been using this approach for the last 6 months and it works as a charm. i would have personally gone with this if i participated. Clear and pure ABAP.
2020 Mar 29 10:55 PM
2020 Mar 17 1:56 PM
Finalist #5: Single Line Solution
By: christian.guenter
out->write( CONV string(
LET sentence = `ABАP is excellent `
match_word_regex = `\w+`
match_duplicate_chars_regex = `(.)(?=.*\1)`
num_of_words = count( val = sentence
regex = match_word_regex )
IN REDUCE #( INIT output = |Number of words: { num_of_words }|
FOR word_index = 1 WHILE word_index <= num_of_words
LET word = match( val = sentence
regex = match_word_regex
occ = word_index )
num_of_unique_chars = numofchar( replace( val = word
regex = match_duplicate_chars_regex
occ = 0
with = space ) )
IN NEXT output = output && |\nNumber of unique characters in word: { word } - { num_of_unique_chars }| ) ) ).
2020 Mar 20 9:44 AM
2020 Mar 20 9:48 AM
But then again,
The Regex makes it easier for constructing the Expression; finding the correct Regex is another story though...
match_word_regex =`\w+`
match_duplicate_chars_regex =`(.)(?=.*\1)`
2020 Mar 17 1:57 PM
Finalist #6: Sorted Table Discarding Duplicates Approach
By: jacques.nomssi
TYPES sorted_char_table TYPE SORTED TABLE OF string WITH UNIQUE DEFAULT KEY.
DO.
TRY.
DATA(word) = segment( val = sentence index = sy-index space = ` ` ).
DATA(characters) = VALUE string_table( FOR idx = 0 UNTIL idx = numofchar( word ) ( word+idx(1) ) ).
DATA(chars_in_word) = CORRESPONDING sorted_char_table( characters DISCARDING DUPLICATES ).
out->write( |Number of unique characters in the word:{ word } - { lines( chars_in_word ) }| ).
CATCH cx_sy_strg_par_val.
out->write( |Number of words:{ sy-index - 1 }| ).
EXIT.
ENDTRY.
ENDDO.
2020 Mar 20 11:30 AM
Hi, I didn't know the DISCARDING DUPLICATES addition. Great, thx
2020 Mar 17 1:58 PM
Finalist #7: Reduce Approach
By: dinumbabu
SPLIT sentence AT ` ` INTO TABLE DATA(words).
DELETE words WHERE table_line IS INITIAL.
out->write( |no of words: { lines( words ) } | ).
LOOP AT words ASSIGNING FIELD-SYMBOL(<word>).
out->write( |Number of unique characters in the word: { <word> } - {
REDUCE i( INIT len = 1
FOR n = 1 WHILE ( n <= strlen( <word> ) - 1 )
NEXT len = len + COND #( WHEN <word>+0(n) CA <word>+n(1) THEN 0 ELSE 1 ) ) } | ).
ENDLOOP.