8

How to translate this snippet of executable pseudo code into ABAP?

phone_numbers = {
    'hans': '++498912345',
    'peter': '++492169837',
    'alice': '++6720915',
}

# access
print (phone_numbers['hans'])

# add
phone_numbers['bernd']='++3912345'

# update
phone_numbers['bernd']='++123456'

if 'alice' in phone_numbers:
    print('Yes, alice is known')

# all entries
for name, number in phone_numbers.items():
    print(name, number)

Modern ABAP is possible up to 752, less chars, more upvotes :-)

P.S. BTW, up to now no one has added abap to pleac (Programming Language Examples Alike Cookbook)

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
guettli
  • 25,042
  • 81
  • 346
  • 663
  • `phone_numbers = VALUE #( ( person = 'hans' phone = '++498912345' ) ( person = ...`, `phone_numbers = VALUE #( BASE phone_numbers ( person = 'bernd' ...`, `IF line_exists( phone_numbers[ person = 'alice' ] )`, `LOOP AT phone_numbers ASSIGNING FIELD-SYMBOL(). print( -name, ...` – Sandra Rossi Dec 04 '18 at 13:46
  • @SandraRossi I think this gets too long for a comment. Why not write an answer? – guettli Dec 04 '18 at 13:54
  • 1
    Is PLEAC worth it? Even Java got only 20.86% covered, C++ even only 8.57%. C# doesn't even turn up, apparently too young. :-) – Florian Dec 04 '18 at 16:30
  • @Florian do you know a better comparison of different programming languages? I have not looked at pleac since years. But I liked it when I switched from Perl to Python which was roughly 2001. – guettli Dec 05 '18 at 08:36
  • 1
    Actually no. Never used such a comparison. But yes, I can imagine it being helpful. – Florian Dec 11 '18 at 23:04

2 Answers2

5

Well, how about the following solution?

REPORT ZZZ.

TYPES: BEGIN OF t_phone_number,
  name TYPE char40,
  number TYPE char40,
  END OF t_phone_number.

DATA: gt_phone_number TYPE HASHED TABLE OF t_phone_number WITH UNIQUE KEY name.

START-OF-SELECTION.
  gt_phone_number = VALUE #(
    ( name = 'hans' number = '++498912345' )
    ( name = 'peter' number = '++492169837' )
    ( name = 'alice' number = '++6720915' )
  ).

* access
  WRITE / gt_phone_number[ name = 'hans' ]-number.

* add
  gt_phone_number = VALUE #( BASE gt_phone_number ( name = 'bernd' number = '++3912345' ) ).

* update
  MODIFY TABLE gt_phone_number FROM VALUE #( name = 'bernd' number = '++123456' ).

  IF line_exists( gt_phone_number[ name = 'alice' ] ).
    WRITE / 'Yes, Alice is known.'.
  ENDIF.

* all entries
  LOOP AT gt_phone_number ASSIGNING FIELD-SYMBOL(<g_phone_number>).
    WRITE: /, <g_phone_number>-name, <g_phone_number>-number.
  ENDLOOP.
Jagger
  • 10,350
  • 9
  • 51
  • 93
  • 1
    Better: `IF line_exists( gt_dictionary[ name = 'alice' ] ).` instead of `READ TABLE ... IF sy-subrc = 0.` – Sandra Rossi Dec 04 '18 at 13:48
  • @SandraRossi You are absolutely right. I will adjust my answer. – Jagger Dec 04 '18 at 13:50
  • The `HASHED` surprised me a bit. When uncertain how the table is modified, I'd suggest to go for a `SORTED` table instead. – Florian Dec 04 '18 at 16:26
  • @Florian I don't understand why it suprised you. The hashed table is the best way to implement an associative array known from other languages. – Jagger Dec 04 '18 at 20:34
  • @Jagger the "add" looks complicated. Is there no easier way? Is there a way to make "add" and "modify" the same code like in Python? – guettli Dec 05 '18 at 08:37
  • @guettli I am afraid not. You could do the add however also in the following way `INSERT VALUE #( name = 'bernd' number = '++3912345' ) INTO TABLE gt_phone_number.` Such an assignment `gt_phone_number[ name = 'bernd' ]-number = '++123456'` will result in a short dump. – Jagger Dec 05 '18 at 09:18
  • @Jagger, HASHED means there will be a long index and that the index is recalculated when the content changes. My experience says to look at the size and lifecycle of the map to determine whether to use HASHED or SORTED. For a handful of entries, or a map that is added to and removed from again and again, or a map that is read only a couple of times, hashing has so much overhead that it wastes memory and processor time for nothing. This is true for most programming languages, but too often ignored. I have an actual case in Java where a simple array is much faster and smaller than HashedSet etc. – Florian Dec 06 '18 at 05:28
  • @Florian: Well the same apply for a sorted table. Each time you add something to such a table, then a suitable place has to be found for it. I think most of the dictionaries are implemented as hashed tables, because the complexity of finding an element in it is O(1). With a sorted array with binary search the complexity is O(logN). – Jagger Dec 06 '18 at 07:32
  • @Jagger, yes, true. Preconditions for HASHED are: lots of data, many reads, primary key access only, few modifications. All of these must be fulfilled. There are dictionary cases where this fits, and others where it doesn't. Let's just be aware of this and prove by measuring that we're using the best data structure. :-) In the Java example I mentioned, an O(n) scan of an unsorted/-hashed standard `List` proved to be much more efficient. There was just not enough data to break even the sorted and hashed variants. ABAP apps rarely read tons of data. – Florian Dec 09 '18 at 15:01
3

@Jagger's answer is great, but @guettli asked for shorter syntax. So just for completeness, there is of course always the possibility to wrap this in a class:

CLASS dictionary DEFINITION.

  PUBLIC SECTION.

    TYPES:
      BEGIN OF row_type,
        key  TYPE string,
        data TYPE string,
      END OF row_type.

    TYPES hashed_map_type TYPE HASHED TABLE OF row_type WITH UNIQUE KEY key.

    METHODS put
      IMPORTING
        key  TYPE string
        data TYPE string.

    METHODS get
      IMPORTING
        key           TYPE string
      RETURNING
        VALUE(result) TYPE string.

    METHODS get_all
      RETURNING
        VALUE(result) TYPE hashed_map_type.

    METHODS contains
      IMPORTING
        key           TYPE string
      RETURNING
        VALUE(result) TYPE abap_bool.

  PRIVATE SECTION.
    DATA map TYPE hashed_map_type.

ENDCLASS.

CLASS dictionary IMPLEMENTATION.

  METHOD put.
    READ TABLE map REFERENCE INTO DATA(row) WITH TABLE KEY key = key.
    IF sy-subrc = 0.
      row->*-data = data.
    ELSE.
      INSERT VALUE #( key  = key
                      data = data )
        INTO TABLE map.
    ENDIF.
  ENDMETHOD.

  METHOD get.
    result = map[ key = key ]-data.
  ENDMETHOD.

  METHOD get_all.
    INSERT LINES OF map INTO TABLE result.
  ENDMETHOD.

  METHOD contains.
    result = xsdbool( line_exists( map[ key = key ] ) ).
  ENDMETHOD.

ENDCLASS.

Leading to:

DATA(phone_numbers) = NEW dictionary( ).

phone_numbers->put( key = 'hans' data = '++498912345' ).
phone_numbers->put( key = 'peter' data = '++492169837' ).
phone_numbers->put( key = 'alice' data = '++6720915' ).

" access
WRITE phone_numbers->get( 'hans' ).

" add
phone_numbers->put( key = 'bernd' data = '++3912345' ).

" update
phone_numbers->put( key = 'bernd' data = '++123456' ).

IF phone_numbers->contains( 'alice' ).
  WRITE 'Yes, alice is known'.
ENDIF.

" all entries
LOOP AT phone_numbers->get_all( ) INTO DATA(row).
  WRITE: / row-key, row-data.
ENDLOOP.

People rarely do this in ABAP because internal tables are so versatile and powerful. From my personal point of view, I'd like to see people build more custom data structures. Implementation details like HASHED or SORTED, see discussion in @Jagger's answer, are hidden away in a natural way when doing this.

Jagger
  • 10,350
  • 9
  • 51
  • 93
Florian
  • 4,821
  • 2
  • 19
  • 44
  • You say "I'd like to see people build more custom data structures.". I like canonical and easy to use data structures which are built-in. If the vendor does not provide it, then there will be hundred different solutions to solve the same thing. Once I learned lists, dicts, sets and string usage in Python, since then every other language feels complicated for me. But don't get me wrong. SAP (netweaver) is great. It solved a lot of fundamental things very professional. – guettli Dec 06 '18 at 09:39
  • I like canonical data structures, too. But I also often find complicated read-write data structures in ABAP code that _should_ be classes, but aren't because "hey, it's only a table, so what". :-) Look at the `PUT` I wrote for example. ABAPers tend to copy-paste this sort of if-it's-not-already-there-insert-it mini-algorithm all over the place, while in fact it should be encapsulated in a method closely connected to the data structure. – Florian Dec 06 '18 at 09:43
  • Nice answer, mostly because I did not know that this is possible: `IF phone_numbers->contains( 'alice' ).`. I always wrote it with suffix `= abap_true`. I have corrected the code to make it compilable. – Jagger Dec 06 '18 at 12:30
  • This syntax is rather new and people haven't widely adopted it, yet. Precondition is that your method returns the data type `abap_bool`. One of the reasons we favor this data type over comparable types such as the commonly used data element `BOOLE_D`. – Florian Dec 09 '18 at 14:50
  • I bought the book "ABAP Cookbook" and on one of the first pages the author did define a custom string library. There is a term called "toilet paper programming". The core is smelling ... let's wrap it. How many thousand helper methods get developed by individual developers to ease the individual needs. But don't get me wrong. ABAP has developed in the past, too. Inline data creating is very nice. Is there a way to solve this at the core (in ABAP), so that no wrapping classes and helper methods are needed? – guettli Dec 13 '18 at 16:33
  • 1
    ABAP's core is developed by a rather small group of people. I find it plausible that they cannot cater to every need. What's really missing in ABAP from my point of view is a convenient way to share code, such that people could write and publish libraries for others to use. – Florian Dec 13 '18 at 22:28