Skip to Content
Technical Articles

Bicycles. #1 – String map

Preamble and disclaimer

Creating own implementation for some problem, which was already solved is before, often referred as “inventing a bicycle”. I did invent a couple too 🙂 This post, and maybe a couple of following posts, describe several of my open-source “bicycles”. I “invented” them because in the time I was searching for such functionalities that I didn’t find them. I’m suspect these functionalities do exist already and you can point that out in comments, if you know a better and more standard solution. Yet my “bicycles” were “invented” with the convenience in mind and with an attempt to make clean and universal tools. So I sincerely hope someone will find them useful. If I’m missing an obvious solution – welcome to comment 🙂 KR. Alexander.

String map

Many other languages have an integrated concept of dictionary – a primitive to store key/value associations. E.g. dict in python, Map (or regular objects) in java script. Even C++ has a very standard template of a Map (though not integrated into the language).

Java script

const map = new Map();
map.set('A', 1);
map.get('A'); // => 1

Python

mydict = {}
mydict["A"] = 1
mydict["A"] # => 1

I didn’t find similarly convenient and widely used primitive in ABAP. Yes, the new 7.4 syntax allows constructs like mytab[ key= ‘xyz’ ]-value which is almost the same … but this mechanism is missing in older ABAP stacks. And even modern ABAP still misses some convenience features like listing the keys and easy conversion between structure types. The syntax may also be a bit excessive. Therefore, I created my own implementation. Below are a couple of examples of usage which should be self explainable.

There are many applications of such a primitive. I’m using it to manage http queries and headers which are a good example of key-value sets. I’m sure you may find own applications in your context.

The code is open-sourced and published under MIT license here: https://github.com/sbcgua/abap-string-map. Can be installed using abapGit.

Functionality and Examples

The class (zcl_abap_string_map) implements `get`, `set`, `has`, `size`, `is_empty`, `delete` functions

data lo_map type ref to zcl_abap_string_map.
lo_map = zcl_abap_string_map=>create( ). " or create object ...

lo_map->set(
  iv_key = 'hello'
  iv_val = 'world' ).
some_var = lo_map->get( 'hello' ). " => 'world'

lo_map->has( 'hello' ). " => abap_true
lo_map->is_empty( ). " => abap_false
lo_map->size( ). " => 1
lo_map->delete( 'hello' ). " deletes 'hello' item
lo_map->clear( ). " deletes all items

`set()` allows chaining

lo_map->set(
  iv_key = 'A'
  iv_val = '1' )->set(
  iv_key = 'B'
  iv_val = '2' ).

The class implements `keys`, `values` methods

data lt_all_keys type string_table.
data lt_all_vals type string_table.

lt_all_keys = lo_map->keys( ). " => ( 'hello' )
lt_all_vals = lo_map->values( ). " => ( 'world' )

implements `to_struc`, `from_struc` to interchange with abap structures

data:
  begin of ls_struc,
    a type string,
    b type abap_bool,
    c type i,
  end of ls_struc.

lo_map->from_struc( ls_struc ). " Converts abap structure to string map
lo_map->to_struc( changing cs_container = ls_struc ). " Converts map to abap structure

" If you have more data entries in the map than fields in the target structure
" use strict( false ) - this skips entries which do not have a matching field
lo_map->strict( abap_false )->to_struc( changing cs_container = ls_struc ).

It implements `from_entries` – this copies entries from a provided param. Importantly, the method accepts any table but the shape of the record must conform to `zcl_abap_string_map=>ty_entry`, namely it must have 2 string attributes for key and value respectively (see the code of `from_entries` for clarification)

types:
  begin of ty_my_key_value,
    key type string,
    value type string,
  end of ty_my_key_value.

data lt_entries type table of ty_my_key_value.
lt_entries = value #(
  ( key = 'hello' value = 'world' )
  ( key = 'and'   value = 'another' )
).
lo_map->from_entries( lt_entries ).

You may set the map immutable (read only). Guards `set`, `delete`, `clear`, `from_*` methods.

lo_map->set(
  iv_key = 'A'
  iv_val = '1' )->freeze( ).

lo_map->set(
  iv_key = 'A'
  iv_val = '2' ). " raises cx_no_check

`create` also supports immediate initiation from another instance of string map, a structure (same as running `from_struc` after) or a table (same as running `from_entries` after)

lo_copy = zcl_abap_string_map=>create( lo_map ).
lo_copy = zcl_abap_string_map=>create( ls_struc ). " see examples above
lo_copy = zcl_abap_string_map=>create( lt_entries ). " see examples above

For more examples see unit tests code.

I hope you find this useful or at least interesting 🙂

Regards, Alexander

 

4 Comments
You must be Logged on to comment or reply to a post.
  • I develop regularly in Java (not JavaScript, however) and using Hashmaps extensively. However, when I develop in ABAP, I find that I’ve never needed a map implementation – although such a thing, as you’ve shown, is fairly simple to put in.

    I use hashmaps in Java to hold look up tables. In ABAP, I just use a HASHED internal table. These are basic types in ABAP, so I don’t see the need to… reinvent the bicycle.

    • I do, and created my own bycicle.

      Compare:

      map->set( key = '2' value = "v"). 

      With

      data:row like line of tab.
      row-key = '1'.
      row-value = 'v'.
      insert row into table itab. 

      On top of wasting 4 lines to enter a value in your map, you are left with an extra variable you only needed for this. 

      Ok, with 7.4 you can do it in one line (I think) , but still the syntax is clunkier

  • There is a lot of stuff in the ABAP stack – and sometimes potentially useful things get overlooked. Perhaps it is time to introduce 

    Class CL_OBJECT_MAP and Interface IF_OBJECT_MAP

    Admittedly, not everything that Alexander has implemented

    /