Application Development 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: 

SAP Community Coding Challenge - March 2020

thomas_jung
Developer Advocate
Developer Advocate

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!

53 REPLIES 53

thomas_jung
Developer Advocate
Developer Advocate

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.

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:

  1. Finalist #1: cl_abap_reger + cl_abap_matcher + find_all + lines
  2. Finalist #2: replace regex + strlen

I'm interested to hear from people the reasons why they prefer Finalist #1 over Finalist #2...

0 Kudos

I would say Finalist #1 code looks more clean, in terms of readability, compared to Finalist #2.

0 Kudos

I would go with this one as the easiest to read.

And it also follows the basis of clean code convention.

0 Kudos

Nice clean solution! +1

thomas_jung
Developer Advocate
Developer Advocate

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.

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!

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.

Agreed. Simple and clean.

I liked the other finalists as well. Neat to see all the difference approaches.

II like this solution. Well besides the variable name

Very clean and straight forwars. I like it.

AlexFrank
Product and Topic Expert
Product and Topic Expert

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/

0 Kudos

Well done but constructing the correct Regex is another story...

thomas_jung
Developer Advocate
Developer Advocate

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.

thomas_jung
Developer Advocate
Developer Advocate

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.

absolutly love it !

perfect solution!

FROM @ works only with HANA

0 Kudos

love this solution!

0 Kudos

perfect solution

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

  • either the SQL statement is really simple (no join, no order by, no distinct, etc.) so it's executed by ABAP kernel,
  • or it's not really simple and it's executed by the database and only HANA currently supports it.

0 Kudos

really a great approach.

congrats!

Amazing, super clean, concise, readable and maintanable 😛 What more can you ask?

0 Kudos

Beautiful than i wrote lol ! I should learn from them...Good job!

BiberM
Active Participant
0 Kudos

Very clever solution! Nothing I would implement in my code base (data transfer overhead for limited benefit) but extra kudo for creativity!

0 Kudos

Selection from the internal table limits the application of this solution. Easier DELETE ADJACENT DUPLICATE ENTRIES FROM characters.

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.

Föß
Active Participant

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!

You should win ... if you don't you still should be given a gold medal for creativity! Well done!!!

0 Kudos

some learning this _/|\_

0 Kudos

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.

0 Kudos

Totally impressed with this solution.

thomas_jung
Developer Advocate
Developer Advocate

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 }| ) ) ).


0 Kudos

Wow! Well done mate.

0 Kudos

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)`

thomas_jung
Developer Advocate
Developer Advocate

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.

Föß
Active Participant
0 Kudos

Hi, I didn't know the DISCARDING DUPLICATES addition. Great, thx

thomas_jung
Developer Advocate
Developer Advocate

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.