Competitive programming with ABAP

Some of the well known competitive questions and my try for solutions on ABAP and HANA

  1. Given 2 tables, a manager table and an employee table, query to get all Managers who have at least one male & one female employee reporting to him.
  2. Given a string as an input parameter to a program, write code to identify if any permutations of the string is Palindrome or not.
  3. Given a table which includes field firstname and lastname, query that returns the first and last name of each person in the table whose last name appears at least twice in the column “lastname”.
  4. Department Top Three Salaries
  5. Count the unique characters in each word of a string.


    These questions are already out there on various platforms like leetcode, however, to find solutions in ABAP is quite hard for such problems.
    I am trying my hands on the solutions on ABAP or HANA for these as these questions are situation based.
    My objective would be to solve with minimal coding as much as possible.

Question 1

Given 2 tables, a manager table and an employee table, query to get all Managers who have at least one male & one female employee reporting to him.

This is a very simple question to begin with. I will be using aggregation functions for calculating employees under a manager.

SELECT a~id,
       a~name,
       SUM( CASE WHEN b~gender = 'F' THEN 1 ELSE 0 END ) as female,
       SUM( CASE WHEN b~gender = 'M' THEN 1 ELSE 0 END ) as male
  FROM zmanager as a
  INNER JOIN zemployee as b
  ON a~id = b~manager
  INTO TABLE @DATA(lt_)
  GROUP BY a~id, a~name.

  DELETE lt_ WHERE ( female < 1 or male < 1 ).



Question 2

Given a string as an input parameter to a program, write code to identify if any permutations of the string is Palindrome or not.

For exmaple:
Given Input: aab
Output: True (aba)

Given Input: abc
Output: False


To calculate all the permutations of the string is basic algorithm with an idea of swapping the character until you reach the end of the string. Source of the inspiration is taken from here.

    REPORT Z_SRTING_PERMUTE_PALINDROME.

CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    TYPES: tt_array TYPE STANDARD TABLE OF c WITH DEFAULT KEY.
    DATA: mt_array TYPE tt_array,
          mt_permute TYPE SORTED TABLE OF string WITH UNIQUE DEFAULT KEY.
    METHODS: string_to_array IMPORTING str             TYPE string
                             RETURNING VALUE(rt_array) TYPE tt_array,
      array_to_string    IMPORTING im_array      TYPE tt_array
                         RETURNING VALUE(rv_str) TYPE string,
      calc_permutations IMPORTING im_str TYPE string
                                 left   TYPE i
                                 right  TYPE i,
      check_palindrome IMPORTING im_str                  TYPE string
                       RETURNING VALUE(rv_is_palindrome) TYPE boolean,
      swap IMPORTING im_str        TYPE string
                     start_index   TYPE i
                     target_index  TYPE i
           RETURNING VALUE(rv_str) TYPE string.


ENDCLASS.

CLASS lcl_main IMPLEMENTATION.
  METHOD string_to_array.
    DATA: lv_index TYPE i.
    DATA: lt_str TYPE TABLE OF c.
    DATA: lv_length TYPE i.

    lv_length = strlen( str ).

    WHILE lv_index < strlen( str ).

      DATA(lv_char) = str|lv_index(1).
      APPEND lv_char TO lt_str.
      ADD 1 TO lv_index.
    ENDWHILE.

    rt_array = lt_str.
  ENDMETHOD.

  METHOD calc_permutations.

    IF left = right.
      IF check_palindrome( im_str ) IS NOT INITIAL.
        INSERT im_str INTO TABLE mt_permute.
      ENDIF.
    ELSE.
      DATA(i) = left.
      WHILE i <= right.

        DATA(lv_str) = swap( EXPORTING im_str = im_str
                                       start_index = left
                                       target_index = i ).
        DATA(lv_left) = left | 1.
        calc_permutations( EXPORTING im_str = lv_str
                                     left = lv_left
                                     right = right ).
        i =  i | 1.
      ENDWHILE.
    ENDIF.

  ENDMETHOD.

  METHOD check_palindrome.
    DATA: lv_reverse TYPE string.

    lv_reverse = reverse( im_str ).

    IF to_lower( im_str ) = to_lower( lv_reverse ).
      rv_is_palindrome = abap_true.
    ELSE.
      rv_is_palindrome = abap_false.
    ENDIF.
  ENDMETHOD.

  METHOD array_to_string.
    LOOP AT im_array INTO DATA(lv_char).
      rv_str = rv_str && lv_char.
    ENDLOOP.
  ENDMETHOD.

  METHOD swap.
    DATA: lv_temp TYPE c.
    DATA: lt_str TYPE TABLE OF c.

    lt_str = string_to_array( im_str ).
    lv_temp = lt_str[ start_index ].
    lt_str[ start_index ] = lt_str[ target_index ].
    lt_str[ target_index ] = lv_temp.

    rv_str = array_to_string( lt_str ).

  ENDMETHOD.
ENDCLASS.


START-OF-SELECTION.

  DATA: lv_str TYPE string VALUE 'carerac'.
  DATA: lt_str TYPE TABLE OF c.
  DATA: lo_obj TYPE REF TO lcl_main.
  DATA: lv_last TYPE c.

  CREATE OBJECT lo_obj.

  lt_str = lo_obj->string_to_array( lv_str ).
  DATA(lv_len) = strlen( lv_str ).

  lo_obj->calc_permutations( EXPORTING im_str = lv_str
                                      left = 1
                                      right = lv_len ).



Question 3

Given a table which includes field firstname and lastname, query that returns the first and last name of each person in the table whose last name appears at least twice in the column “lastname”.

I have tried to do this using subquery approach.

SELECT name, lastname FROM zemployee
INTO TABLE @DATA(lt1_)
WHERE lastname IN
 ( SELECT lastname FROM zemployee
  GROUP BY lastname HAVING COUNT(*) > 1 ).



Question 4

Department Top Three Salaries

The Employee table holds all employees. Every employee has an Id, and there is also a column for the department Id.

Id Name Salary DepartmentId
1 Joe 85000 1
2 Henry 80000 2
3 Sam 60000 2
4 Max 90000 1
5 Janet 69000 1
6 Randy 85000 1
7 Will 70000 1

The Department table holds all departments of the company.

Id Name
1 IT
2 Sales

Write a SQL query to find employees who earn the top three salaries in each of the department. For the above tables, your SQL query should return the following rows (order of rows does not matter).

Department Employee Salary
IT Max 90000
IT Randy 85000
IT Joe 85000
IT Will 70000
Sales Henry 80000
Sales Sam 60000


Explanation:

In IT department, Max earns the highest salary, both Randy and Joe earn the second highest salary, and Will earns the third highest salary. There are only two employees in the Sales department, Henry earns the highest salary while Sam earns the second highest salary.

This is possible in number of ways in ABAP.
However, I tried to make use of HANA in this case to get the most out of the query with least time. 
So I ended up creating a CDS Table Function which calls AMDP in the HANA.

Syntax that I have used in this has been referenced from here.

@EndUserText.label: 'Select top 3 salary from departments'
define table function ZSELECT_TOP
returns {
  clnt : abap.clnt;
  name : abap.char(40);
  salary: abap.int4;
  department: abap.int2;
  
}
implemented by method cl_test_amdp=>get_data;
CLASS cl_test_amdp DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES if_amdp_marker_hdb.
    CLASS-METHODS: 
      get_data FOR TABLE FUNCTION ZSELECT_TOP.

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS cl_test_amdp IMPLEMENTATION.
METHOD get_data BY DATABASE FUNCTION FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY
  USING zemployee.

    lt_temp = select mandt as clnt, name, salary, department,
            dense_rank() OVER (partition by department order by salary desc) as rank
            FROM zemployee;

            return select clnt, name, salary, department
            from :lt_temp
            where rank <= 3
            order by department asc;

  ENDMETHOD.
ENDCLASS.



Let’s see some more string manipulations as it tests a lot of knowledge.

Question 5

Count the unique characters in each word of a string.

Now this question was taken from ABAP community challenge .
Challenge was to write the code in less than 9 lines (Including input and output lines).
Again there are multiple ways with which it can be achieved. To achieve this in less than 9 lines, I gave a shot to Regular expressions.

DATA(sentence) = `ABАP is excellent `.
SPLIT sentence AT space INTO TABLE DATA(lt_text).
LOOP AT lt_text INTO DATA(lv_text).
  DATA(lv_result) = strlen( lv_text ) - count( val   = lv_text regex = `(.)(?=.*\1)` ).
  WRITE:/ |No of unique characters in the word: { lv_text } - { lv_result }|.
ENDLOOP.



If you liked the questions or have any query, do let me know in the comments sections.
Happy learning!