Skip to Content
Technical Articles

My Solution to ABAP Community Code Contest `ABAP is excellent `

When Thomas Jung started the ABAP Community Code Challenge on February 28th, I first thought: Okay, this is not a big deal for an advanced programmer. But after the first solutions came up with “made it with 5 lines”, this really challenged me to do this in one line. After some help of my friends Peter Jaeckel and Łukasz Pęgiel I really made it in one line.

Solution

This is my solution:

"Explanation why ABAP has four unique characters and not three as expected:
"the second A is a cyrillic character which looks equal to "normal A" but has different unicode value

PARAMETERS p_text type string DEFAULT `ABАP  is excellent ` LOWER CASE.

"class (S)AP (C)ommunity (C)oding (C)hallenge (S)eries
CLASS scccs DEFINITION.
  PUBLIC SECTION.
    METHODS solve
      IMPORTING
        text          TYPE string
      RETURNING
        VALUE(result) TYPE string_table.
ENDCLASS.

CLASS test DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.

  PUBLIC SECTION.
  PRIVATE SECTION.
    DATA cut TYPE REF TO scccs.
    METHODS setup.
    METHODS abap_is_excellent FOR TESTING.
    METHODS lorem_ipsum_dolor_sit_amet FOR TESTING.
    METHODS many_trailing_spaces FOR TESTING.
    METHODS many_leading_spaces FOR TESTING.
    METHODS many_space_in_the_middle FOR TESTING.
    METHODS complex FOR TESTING.

ENDCLASS.

CLASS scccs IMPLEMENTATION.
  METHOD solve.

    result = REDUCE string_table( "STRING_TABLE is the output...
             LET
               "clean the text from spaces
               text_wo_double_spaces = replace( regex = '(\s)(?=\1)' val = text  with = `` occ = 0 )
               "delete trailing and leading blanks
               cleaned_text = shift_left( val = shift_right( val = text_wo_double_spaces ) )
               "number of words is the number of blanks + 1
               num_words = count_any_of( val = cleaned_text sub = ` ` ) + 1 IN
             INIT
               "fill first result line with "number of words"
               lines = VALUE #( ( |Number of words: { num_words }| ) )
             " derive words via SEGMENT of cleaned string
             FOR word
             IN REDUCE string_table(
             INIT w = VALUE #( )
             "do read the words and store in table (equals SPLIT INTO TABLE)
             FOR i = 1 THEN i + 1 UNTIL i > num_words
             NEXT w = VALUE #( BASE w ( segment( val = cleaned_text index = i ) ) ) )
             NEXT lines = VALUE #(
               "transfer word-string to result table (BASE for keeping existing lines)
               BASE lines ( |Number of unique characters in the word: { word } - {
                    "now calculate number of distinct characters in each word
                    lines( REDUCE string_table(
                      "prepare the character table
                      INIT chars = VALUE string_table( )
                      "do aas many times as the word has characters; pos is the offset for current character
                      FOR pos = 0 THEN pos + 1 WHILE pos < strlen( word )
                      NEXT chars = COND #(
                        "if current character does not exist in character table,...
                        WHEN NOT line_exists( chars[ table_line = word+pos(1) ] )
                        "...then insert current character
                        THEN VALUE #( BASE chars ( word+pos(1) ) )
                        " otherwise keep current character table as it is
                        ELSE chars ) ) ) }| ) )  ) .

  ENDMETHOD.
ENDCLASS.

CLASS test IMPLEMENTATION.
  METHOD setup.
    cut = NEW #( ).
  ENDMETHOD.

  METHOD abap_is_excellent.

    cl_abap_unit_assert=>assert_equals(
        act = cut->solve( `ABАP  is excellent ` )
        exp = VALUE string_table(
           ( `Number of words: 3` )
           ( `Number of unique characters in the word: ABАP - 4` )
           ( `Number of unique characters in the word: is - 2` )
           ( `Number of unique characters in the word: excellent - 6` ) ) ).

  ENDMETHOD.

  METHOD lorem_ipsum_dolor_sit_amet .

    cl_abap_unit_assert=>assert_equals(
        act = cut->solve( `Lorem ipsum dolor sit amet` )
        exp = VALUE string_table(
           ( `Number of words: 5` )
           ( `Number of unique characters in the word: Lorem - 5` )
           ( `Number of unique characters in the word: ipsum - 5` )
           ( `Number of unique characters in the word: dolor - 4` )
           ( `Number of unique characters in the word: sit - 3` )
           ( `Number of unique characters in the word: amet - 4` )
            ) ).

  ENDMETHOD.

  METHOD many_trailing_spaces.
    cl_abap_unit_assert=>assert_equals(
        act = cut->solve( `start             ` )
        exp = VALUE string_table(
           ( `Number of words: 1` )
           ( `Number of unique characters in the word: start - 4` )
            ) ).
  ENDMETHOD.

  METHOD many_leading_spaces.
    cl_abap_unit_assert=>assert_equals(
        act = cut->solve( `             end` )
        exp = VALUE string_table(
           ( `Number of words: 1` )
           ( `Number of unique characters in the word: end - 3` )
            ) ).
  ENDMETHOD.

  METHOD many_space_in_the_middle.
    cl_abap_unit_assert=>assert_equals(
        act = cut->solve( `start          end` )
        exp = VALUE string_table(
           ( `Number of words: 2` )
           ( `Number of unique characters in the word: start - 4` )
           ( `Number of unique characters in the word: end - 3` )
            ) ).
  ENDMETHOD.

  METHOD complex.
    cl_abap_unit_assert=>assert_equals(
        act = cut->solve( `    one   two      thréé  ÄÖÜ  •••   ` )
        exp = VALUE string_table(
           ( `Number of words: 5` )
           ( `Number of unique characters in the word: one - 3` )
           ( `Number of unique characters in the word: two - 3` )
           ( `Number of unique characters in the word: thréé - 4` )
           ( `Number of unique characters in the word: ÄÖÜ - 3` )
           ( `Number of unique characters in the word: ••• - 1` )
            ) ).
  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.
  cl_demo_output=>display_data( NEW scccs( )->solve( p_text ) ).

To make it look more professional, I added some unit tests. 😉

I tried to explain each single statement inline as good as I was able to.

Although I used unit tests, clean code and only one line, I didn’t make it to the finals. 🙁

Maybe next time…

P.S.: Unfortunately I forgot to release this blog post… 🙁

P.S.S.: here is the Current Community Coding Challenge

That also reminds me to complete the Advent Of Code 2019

20 Comments
You must be Logged on to comment or reply to a post.