Technical Articles
My learning journey with ABAP SAP Community Coding Challenge #1
When the ABAP community coding challenge was first announced, the only thing that I thought was how to write in as fewer lines as possible 😀
Is it good to write like that? No, I don’t think so and you all know the reason
But did I have fun writing like that? Hell yeah!!
And the best thing I have done is that I didn’t stop trying out new ways after I submitted my first solution, I read the documentation many times to improve my approaches and I ended up sending 5 emails with 5 different solutions.
Although I didn’t make it to finals, I really loved and enjoyed doing this challenge.
Now I will show the 5 different approaches I used to solve this challenge.
Solution #1
Highlights:
- One-Line solution
- Used REGEX
- Used CL_ABAP_REGEX class ( 😀 )
- Most Ugliest?
out->write( data = VALUE string_table(
" Declaring the string & extracting the words from the string
LET sentence = `ABАP is excellent `
words = VALUE string_table(
FOR rawword IN NEW cl_abap_regex( pattern = '(\b\w+\b)'
)->create_matcher( text = sentence )->find_all( )
( substring( val = sentence off = rawword-offset len = rawword-length ) ) )
" Filling the below additional value using base as the below for loop will
" only get executed max words times,
" But our output needs max words + 1 lines
IN BASE VALUE string_table( ( |Number of Words: { lines( words ) }| ) )
" Loop all the words and extract the unique characters
FOR word IN words ( |Number of unique characters in the word: { word } -
{ lines( NEW cl_abap_regex( pattern = '(\w)(?!.*\1)'
)->create_matcher( text = word )->find_all( ) ) }| )
) ).
I really thought I cracked the SAP coding challenge after doing this solution there 😀
As I was not aware of regex compatible string functions that were available at that point in time, I tried using CL_ABAP_REGEX class, it worked surprisingly. You can see that I used create_matcher and find_all to figure out pattern matches.
And I used “FOR” to loop and fill the final string table (no concatenation) and I used a similar approach in all other solutions.
Solution #2
So after going through the documentation, again and again, I found that I can use String Functions that support REGEX and I now can completely remove the usage of the above classes.
Highlights:
- One-Line Solution
- Regex String functions
out->write( data = VALUE string_table(
" Data declaration & finding the total no of words
LET sentence = `ABАP is excellent `
totalWords = count( val = sentence regex = `(\b\w+\b)` )
" Filling the base table with Total no of words as we will loop words times
IN BASE VALUE string_table( ( |Number of Words: { totalWords }| ) )
" Looping total words for unique characters count
FOR i = 1 UNTIL i > totalWords (
" Getting the word
CONV #( LET word = match( val = sentence regex = '(\b\w+\b)' occ = i )
" Getting the count of unique characters
IN |Number of unique characters in the word: { word } -
{ count( val = word regex = `(\w)(?!.*\1)` ) } | ) ) ) ).
The usage of “Count” and “Match” replaced all that class-related code and now it became a lot cleaner.
Solution #3
So after that, I thought enough is enough with the “Regex” and tried to find a different solution that doesn’t use it.
Highlights:
- Not a One-Line Solution
- No Regex
- Finding Unique characters using “GROUPS” that auto sorts and gives unique records
" Get the total no of words
SPLIT condense( sentence ) AT space INTO TABLE DATA(words).
" Output
out->write( data = VALUE string_table(
" Filling the base table with Total no of words
BASE VALUE string_table( ( |Number of Words: { lines( words ) }| ) )
" Looping words to find individual words unique character count
FOR word IN words ( |Number of unique characters in the word:
{ word } - { lines( VALUE string_table(
" Grouping characters -> group will auto sort and find the unique records
FOR GROUPS char_group OF char IN VALUE string_table(
" For filling character table to group it in the above statement,
" we need to loop string length times
FOR char_pos = 1 UNTIL char_pos > strlen( word )
" Now getting individual characters
( substring( val = word off = char_pos - 1 len = 1 ) ) )
" filling the character table for grouping
GROUP BY char ( char_group ) ) ) }| ) ) ).
As I thought, there is no other way to find the total no of words, I ended up using condense & Split to get the total no of words.
I used “GROUP BY” to find unique characters. AIthoug I used “GROUP BY” many times in my projects, but didn’t realize till that time that I could use it that way.
Solution #4
After Thomas Jung and few others were mentioning the importance of a clean solution and stuff, I thought I should do it in a simple readable approach. and here it is
Highlights:
- Not a One-Line Solution
- a simple readable approach
DATA(sentence) = `ABАP is excellent `.
SPLIT condense( sentence ) AT space INTO TABLE DATA(words).
out->write( data = |Number of Words: { lines( words ) }| ).
LOOP AT words REFERENCE INTO DATA(word).
DATA(characters) = VALUE string_table( FOR i = 1 UNTIL i > strlen( word->* )
( substring( val = word->* off = i - 1 len = 1 ) ) ).
SORT characters.
DELETE ADJACENT DUPLICATES FROM characters.
out->write( data = |Number of unique characters in the word: { word->* }
- { lines( characters ) }| ).
ENDLOOP.
Solution #5 (Final)
This one is the final solution that I submitted and the one that I liked the most. I found that I don’t need to split the words outside anymore and can use the “Segment” string function to get the word & count space hack to find the total number of words.
Highlights:
- One-Line Solution
- My Favourite one (though it’s still ugly 😀 )
- No Regex
out->write( data = VALUE string_table(
LET sentence = `ABАP is excellent `
" Condensed sentence
condensedSentance = condense( sentence )
" Total no of spaces + 1 equals to total no of words
totalNoOfWords = count( val = condensedSentance sub = ` ` ) + 1
" Filling the base table with Total no of words
IN BASE VALUE string_table( ( |Number of Words: { totalNoOfWords }| ) )
" Looping words to find individual words unique character count
FOR wordNo = 1 UNTIL wordNo > totalNoOfWords
LET word = segment( val = condensedSentance index = wordNo sep = ` ` )
uniqueChars = VALUE string_table(
" Grouping characters -> group will auto sort and find the unique records
FOR GROUPS charGrp OF char IN VALUE string_table(
" For filling character table to group it in the above statement
FOR charPos = 0 UNTIL charPos = strlen( word ) ( word+charPos(1) ) )
" filling the character table with unique chars resulted after grouping
GROUP BY char ( charGrp ) )
" Filling the string table for showing the ouput
IN ( |Number of unique characters in the word: { word } - { lines( uniqueChars ) }| ) ) ).
So at the end of this challenge, I learned a lot about the usage of Regex, string functions & many new ABAP syntaxes.
I felt very confident after doing this challenge and later I even answered some questions related to Regex, string functions & new ABAP syntax 🙂 . But still, there is so much to learn, for e.g., check this awesome solution from Sandra Rossi, I was like (Mind = Blown), and took so much time to understand that 😀
https://answers.sap.com/questions/13058395/add-a-check-or-continue-in-a-for-loop-expression.html
Though I was using new ABAP in my projects from long back, this challenge made me explore more possibilities with the new ABAP syntax (especially because I tried to solve this challenge in one line). I would surely have not explored all these if I would have not tried a One-Liner Solution and most importantly the fun I had doing this n:)
I also had fun doing the coding challenge #2, maybe I will share it in another blog post 🙂
That’s it folks and thanks for reading this 🙂
-Mahesh
Well done Mahesh ! Special kudos to your efforts and quest to improve, it took me some time to understand all 5 solutions 🙂
Thanks Gaurav and to be frank, it was a bit difficult for me also to understand some of these while I was copy pasting from the mail to write this post ?. Good that I have provided comments initially itself..