Skip to Content
Event Information

SAP Community Coding Challenge Series – March 2020 – Special Awards

We had a lot of fun doing the very first SAP Community Coding Challenge with all of you. I think we all learned a lot from each other which is what its all about.  We had about 200 submissions and it was really tough to narrow down the finalist which we announced earlier today.  Vote for your favorite here!  We wanted to take a moment and recognize some of the submissions that we think deserve special awards. Remember this is all in good fun, so don’t take offense.  😊

Brain Pain Award

Submitted by Sergey Chebotarev

Yes, we call this one the Brain Pain Award, because when Tom and I read through it, our brains were in pain.  This example used one line of code for the solution leveraging several in-lined ABAP statements including REDUCE, FOR loops, and conditional statements.  I think we are very much impressed that someone actually wrote this code and understands how it is working. Impressive Sergey!

METHOD if_oo_adt_classrun~main.
    out->write( |Brain Pain| ).
    out->write( |https://people.sap.com/mobui| ).
    out->write( |Сергей Чеботарев - Sergey Chebotarev| ).
    DATA(sentence) = `ABАP  is excellent `.

    out->write( REDUCE string_table( INIT
             full_result TYPE string_table
             result  TYPE string_table
             word_count = 0
             cur_word = ``
             cur_word_uniq_len = 0
        FOR i = 1  THEN i + 1 WHILE i LE strlen( sentence )
        LET
             i_cur_offset = i - 1
             i_prev_offset = i - 2
             char_cur = CONV char1( sentence+i_cur_offset(1) )
             char_prew = COND char1( WHEN i_prev_offset GE 0 THEN sentence+i_prev_offset(1) ELSE space )
             is_end_of_string = COND #( WHEN i EQ strlen( sentence ) THEN  abap_true ELSE abap_false )
             is_begin_of_word = COND #( WHEN char_cur NE space AND char_prew EQ space THEN abap_true ELSE abap_false )
             is_end_of_word = COND #( WHEN char_cur EQ space AND char_prew NE space OR is_end_of_string EQ abap_true THEN abap_true ELSE abap_false )
        IN NEXT
        word_count = word_count + COND #( WHEN is_begin_of_word EQ abap_true THEN 1 ELSE 0  )
        cur_word_uniq_len = COND #( WHEN is_begin_of_word EQ abap_true THEN 1 ELSE cur_word_uniq_len + COND #( WHEN char_cur NE space AND NOT ( cur_word  CA char_cur )  THEN 1 ELSE 0   ) )
        cur_word = COND #( WHEN is_begin_of_word EQ abap_true THEN  char_cur ELSE COND #( WHEN char_cur NE space THEN cur_word && char_cur ELSE cur_word   ) )
        result = COND #( WHEN is_end_of_word EQ abap_true THEN CORRESPONDING #( BASE ( result ) VALUE string_table( ( | Number of unique characters in the word: { cur_word } - { cur_word_uniq_len } | ) ) )
                                           ELSE  result )
        full_result = COND #( WHEN is_end_of_string  EQ abap_true THEN CORRESPONDING #( BASE ( VALUE #( ( | Number of words: { word_count }| ) ) ) result ) ELSE full_result )
    ) ).

  ENDMETHOD.

Kobayashi Maru Award

Submitted by Matt Harding

This submission comes to us from our good friend Matt Harding.  Of course, his solution was to simply break the rules of the challenge and go his own way with code that is not actually implemented.  Well done Matt. 😊

  METHOD if_oo_adt_classrun~main.
    out->write( |Changed the Rules| ).    
    out->write( |https://people.sap.com/matt.harding| ).
    out->write( |Matt Harding| ).
    DATA(sentence) = `ABАP  is excellent `.

    out->write(`The number of words in the provided sentence is: ` & count_words(sentence)).
    LOOP AT split_sentence_into_words(sentence) reference into DATA(word_reference).
      out->write(`The number of unique characters in the word ` & word_reference->* & ` is: ` & count_chars_in_word(word_reference->*).
    ENDLOOP.

  ENDMETHOD.

Crazy Binary Approach Award

Submitted by Karsten Bohlmann

Here Karsten is doing a one liner REDUCE statement with bit-wise operators which was the only submission using bit-wise operators. Bit wise operation is something that most ABAPers never even mess around with. This is an impressive attempt at low level processing.

METHOD if_oo_adt_classrun~main.
    out->write( |Binary Approach| ).    
    out->write( |https://people.sap.com/karsten.bohlmann| ).
    out->write( |Karsten Bohlmann| ).
    DATA(sentence) = `ABАP  is excellent `.

    out->write( REDUCE string(
  LET words = condense( sentence ) " beware: might be empty
      s = COND #( WHEN words <> `` THEN words && ` ` ) " add trailing space
      zero = CONV xstring( `00` ) " needed later
  IN
  INIT  text = |Number of words: { count_any_of( val = s sub = ` ` ) }|
        k = 0 " offset of 1st word
  FOR   i = find_any_of( val = s sub = ` ` )             " end of 1st word
  THEN      find_any_of( val = s sub = ` ` off = i + 1 ) " end of next word
  WHILE i > 0
  NEXT  text = CONV #(
    LET wlen = i - k " length of current word
        word = substring( val = s off = k len = wlen )
        bits = REDUCE xstring( " (bit-)set of all codepoints in word
                INIT x = VALUE #( )
                FOR  j = 0 UNTIL j >= wlen
                NEXT x = x BIT-OR bit-set( cl_abap_conv_out_ce=>uccpi(
                          substring( val = word off = j len = 1 ) ) ) )
        blen = xstrlen( bits ) * 8 " max. index of a '1' bit
        bcnt = REDUCE i( " count '1' bits in xstring (= size of set)
                INIT b = 0
                FOR  j = 0 UNTIL j > blen
                NEXT b = b + COND #( WHEN bits BIT-AND bit-set( j ) <> zero THEN 1 ) )
    IN    |{ text }\nNumber of unique characters in the word: | &
          |{ word } - { bcnt }| ) " [{ bits }]| )
        k    = i + 1 " offset of next word
)
     ).
  ENDMETHOD.

Now Go Take a Shower Award

Submitted by Morten Wittrock

While Morten did provide a solution in ABAP, he also sent us a solution for Java as well.  And of course after reading through it, we wanted to go shower off and for this reason we present the “Now Go Take a Shower Award” to Morten Wittrock.

 String sentence = "ABАP  is excellent ";
 String[] words = sentence.split("\\s+");
 System.out.printf("Number of words: %d%n", words.length);
    for (String word : words) {
        System.out.printf("Number of unique characters in the word: %s: %d%n", word, word.chars().distinct().count());
    }

Kitchen Sink Award

Submitted by Andreas Bork

We liked this submission because it had a little bit of everything in it, REDUCE, FOR loop, SWITCH statements, and RegEx.  This solution shows a good working knowledge of the statements used and how they can be used together. Good work Andreas!

  METHOD if_oo_adt_classrun~main.
    out->write( |Kitchen Sink| ).    
    out->write( |https://people.sap.com/andreas.bork| ).
    out->write( |Andreas Bork| ).
    DATA(sentence) = `ABАP  is excellent `.

    out->write( VALUE string_table( FOR i = 0 UNTIL i = 4 LET lv_text   = SWITCH string( i WHEN 0 THEN TEXT-001 ELSE |{ TEXT-002 }| )
                                                                         lv_match  = SWITCH string( i WHEN 0 THEN '' ELSE match( val = sentence regex = '\w+' occ = i ) )
                                                                         lv_count  = count( val = sentence regex = '\w+' )
                                                                         lv_length = SWITCH i( i WHEN 0 THEN 0
                                                                                                 ELSE strlen( REDUCE string( INIT lv_letter = `` FOR j = 0 THEN j + 1 UNTIL j > strlen( lv_match ) - 1
                                                                                                                             NEXT lv_letter = lv_letter && COND #( WHEN find( val = lv_letter sub = lv_match+j(1) ) < 0
                                                                                                                                                                   THEN lv_match+j(1) ELSE '' ) ) ) )
                                               IN ( |{ lv_text } { SWITCH string( i WHEN 0 THEN lv_count ELSE |{ lv_match } - { lv_length }| ) } | ) )
     ).
  ENDMETHOD.

Teacher’s Pet Award

Submitted by Jorge Sancho

We wanted to give a special award for the submission which came close to the same solution that Tom and I came up with.  This code is pretty close, the FOR loop is a little different, and we actually in-lined the CONDENSE in the SPLIT statement to collapse those two lines into one line.  Congrats Jorge for being the Teacher’s Pet. 😊

  METHOD if_oo_adt_classrun~main.
    out->write( |Teachers Pet| ).    
    out->write( |https://people.sap.com/jorge.sanchoroyo2| ).
    out->write( |Jorge Sancho| ).
    DATA(sentence) = `ABАP  is excellent `.

    DATA lt_characters TYPE SORTED TABLE OF string WITH NON-UNIQUE KEY table_line.
    CONDENSE sentence.
    SPLIT sentence AT space INTO TABLE DATA(lt_words).
    out->write( |Number of words { lines( lt_words ) }| ).
    LOOP AT lt_words INTO DATA(ld_word).
      lt_characters  = VALUE #( FOR j = 0 WHILE ( j <= strlen( ld_word ) - 1 ) (  ld_word+j(1)  ) ).
      DELETE ADJACENT DUPLICATES FROM lt_characters.
      out->write( |Number of unique characteres in the word: { ld_word } - { lines( lt_characters ) }| ).
    ENDLOOP.
  ENDMETHOD.

Test Class Award

Submitted by James Burngay

Out of 200 or so submissions, there were only 3 or 4 that included test classes in their solution. So we thought we should highlight the submission with the most robust test classes here.  Nice work James!

interface YIF_WORD
  public .
    methods get_text
            returning value(word_text) type string.
    methods get_numberof_unique_characters
            returning value(numberof_unique_characters) type i.
endinterface.
 

interface YIF_SENTENCE
  public.
    methods get_text
            returning value(sentence_text) type string.
    methods get_number_of_words
            returning value(number_of_words) type i.
    methods get_word
            importing index type i
            returning value(word) type ref to yif_word.
endinterface.


CLASS ycl_word DEFINITION

  PUBLIC
  FINAL
  CREATE PUBLIC .

 PUBLIC SECTION.
    interfaces yif_word.
    aliases get_numberof_unique_characters for yif_word~get_numberof_unique_characters.
    aliases get_text for yif_word~get_text.
    methods constructor
            importing text_input type string.

PROTECTED SECTION.
PRIVATE SECTION.
    data text type string.
    data unique_character_count type i.
    methods count_unique_characters.
ENDCLASS.

CLASS ycl_word IMPLEMENTATION.
  METHOD constructor.
    text = text_input.
    count_unique_characters( ).
  ENDMETHOD.
  METHOD count_unique_characters.
    unique_character_count = count( val = text regex = `(\S)(?!.*\1.*)` ).
  ENDMETHOD.
  METHOD yif_word~get_numberof_unique_characters.
    numberof_unique_characters = unique_character_count.
  ENDMETHOD.
  METHOD yif_word~get_text.
    word_text = text.
  ENDMETHOD.
ENDCLASS.

class ltcl_word definition final for testing

  duration short
  risk level harmless.
  private section.

    methods return_word for testing.
    methods empty_word  for testing.
    methods all_letters for testing.
    methods all_numbers for testing.
    methods all_symbols for testing.
    methods mixed_characters for testing.
    methods mixed_case for testing.
    methods repeating_letters for testing.
    methods repeating_letters_mixed_case for testing.
    methods repeating_numbers for testing.
    methods repeating_symbols for testing.
endclass.

class ltcl_word implementation.

  method return_word.
    data(cut) = new ycl_word( `testing` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_text( ) exp = `testing` ).
  endmethod.
  method all_letters.
    data(cut) = new ycl_word( `testing` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 6 ).
  endmethod.
  method all_numbers.
    data(cut) = new ycl_word( `1234567` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 7 ).
  endmethod.
  method all_symbols.
    data(cut) = new ycl_word( `!@#$%^)` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 7 ).
  endmethod.
  method empty_word.
    data(cut) = new ycl_word( `` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 0 ).
  endmethod.
  method mixed_characters.
    data(cut) = new ycl_word( `we!c0me` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 6 ).
  endmethod.
  method mixed_case.
    data(cut) = new ycl_word( `TestS` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 5 ).
  endmethod.
  method repeating_letters.
    data(cut) = new ycl_word( `bananas` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 4 ).
  endmethod.
  method repeating_letters_mixed_case.
    data(cut) = new ycl_word( `BaNAnaS` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 6 ).
  endmethod.
  method repeating_numbers.
    data(cut) = new ycl_word( `111222` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 2 ).
  endmethod.
  method repeating_symbols.
    data(cut) = new ycl_word( `Help!!!!!` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_numberof_unique_characters( ) exp = 5 ).
  endmethod.
endclass.


CLASS ycl_sentence DEFINITION

  PUBLIC
  FINAL
  CREATE PUBLIC .

PUBLIC SECTION.
    interfaces yif_sentence.
    aliases get_number_of_words for yif_sentence~get_number_of_words.
    aliases get_text for yif_sentence~get_text.
    aliases get_word for yif_sentence~get_word.
    methods constructor
            importing text_input type string.

PROTECTED SECTION.
PRIVATE SECTION.
    data text type string.
    data word_count type i.
    methods count_words.
ENDCLASS.

 

CLASS ycl_sentence IMPLEMENTATION.
  METHOD constructor.
    text = text_input.
    count_words( ).
  ENDMETHOD.

  METHOD count_words.
    word_count = count( val = condense( text ) regex = `(\b[^\s]+\b)` ).
  ENDMETHOD.

  METHOD yif_sentence~get_number_of_words.
    number_of_words = word_count.
  ENDMETHOD.

  METHOD yif_sentence~get_text.
    sentence_text = text.
  ENDMETHOD.

  METHOD yif_sentence~get_word.
    word = new ycl_word( cond #( when index between 1 and word_count then segment( val = text index = index space = ` ` ) else `` ) ).
  ENDMETHOD.
ENDCLASS.

 

class ltcl_sentence definition final for testing
  duration short
  risk level harmless.

  private section.
    methods return_sentence for testing.
    methods return_word for testing.
    methods index_out_of_bounds for testing.
    methods one_word for testing.
    methods no_words for testing.
    methods multiple_words for testing.
    methods multiple_spaces for testing.

endclass.

class ltcl_sentence implementation.
  method return_sentence.
    data(cut) = new ycl_sentence( `Hello World` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_text( ) exp = `Hello World` ).
  endmethod.
  method multiple_spaces.
    data(cut) = new ycl_sentence( `Hello    World   ` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_number_of_words( ) exp = 2 ).
  endmethod.
  method multiple_words.
    data(cut) = new ycl_sentence( `Hello World and Hello again World` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_number_of_words( ) exp = 6 ).
  endmethod.
  method no_words.
    data(cut) = new ycl_sentence( `` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_number_of_words( ) exp = 0 ).
  endmethod.
  method one_word.
    data(cut) = new ycl_sentence( `Hello` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_number_of_words( ) exp = 1 ).
  endmethod.
  method index_out_of_bounds.
    data(cut) = new ycl_sentence( `Hello World` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_word( 3 )->get_text( ) exp = `` ).
  endmethod.
  method return_word.
    data(cut) = new ycl_sentence( `Hello World` ).
    cl_abap_unit_assert=>assert_equals( act = cut->get_word( 2 )->get_text( ) exp = `World` ).
  endmethod.
endclass.


CLASS ycl_sap_cc_20200228_clean DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

PUBLIC SECTION.
    interfaces if_oo_adt_classrun.

PROTECTED SECTION.
PRIVATE SECTION.
    types word_element type ref to yif_word.
ENDCLASS.

CLASS ycl_sap_cc_20200228_clean IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    DATA(sentence) = new ycl_sentence( `ABАP  is excellent ` ).
    out->write( reduce stringtab(
                init output = value #( ( |Number of words: { conv string( sentence->get_number_of_words( ) ) }| ) )
                     word type word_element
                for index = 1 until index > sentence->get_number_of_words( )
                next word = sentence->get_word( index )
                     output = value #( BASE output ( |Number of unique characters in the word: { word->get_text( ) } - { conv string( word->get_numberof_unique_characters( ) ) }| ) ) ) ).

  ENDMETHOD.

ENDCLASS.

 

Again congratulations to the finalist as well as to the special award winners.  Great job ABAPers! Remember to vote for your favorite here and stay tuned for the next challenges in the coming months.

15 Comments
You must be Logged on to comment or reply to a post.
  • I’d like to thank the academy first of all for my “special” award (love it) but highlight that I did fix your UX (well the grammar at least) and do we really need to know about the implementation details?

    More seriously though – that SQL solution rocks!

    BTW – In my code is at least 1 hidden request for an ABAP syntax feature too (subliminal feature request)…

    Great initiative.

    Cheers,

    Matt

  • Uhm… thanks, I guess? 😉 As I mentioned to Tammy Powlas, I added the Java solution for a laugh. I’m pretty psyched, though, to see that my ABAP code isn’t miles from the sample solution. I’m pretty happy with that, especially since I’m not a skilled ABAP developer by any measure.

    Thanks again for a very nice coding challenge! I’m looking forward to the next one.

    Regards,

    Morten

  • I am happy to see a proposal with ABAP Unit Test !

    I am not agree with the results, if the objective was to propose A Clean Code with less statement; all the proposal without Class / Interface should not be accepted. Same for the method doing several things at the same time.

    I hope for the next Challenge the rules will be more clear

    I hope we will have another challenge !!

     

    Thanks for the team organize the Challenge

     

  • I thought that everyone in the solution would use split and regular expressions. I wanted to write a solution without them. Well, in one line.

    Thanks for the team organize the Challenge

  • Nice collection 🙂

    Unit test approach is really cool. The names of the special awards are very creative. Especially the “Brain Pain Award” 🙂

    Congratulation to the Awards 🙂

     

  • Congratulations to all!  It’s obvious that everyone took some time to think about their submissions!  It makes it a lot of fun to see them.

    A big thank you to our judges.

  • Hey, why “crazy”? Xstring is just a nice low-cost Set. Works well for “set of characters”.

    My intention was to really use ABAP only and not sidestep the problem by giving it to, say, regexes 🙂

    Nevermind, I’ll take the award. Thanks! 🙂

    • Ahh, didn’t mean much behind the “crazy”, just trying to keep it fun.  🙂  Just don’t think many have ever thought of using that approach. Good work!

      Cheers,

      Rich