MAP

<< Click to Display Table of Contents >>

Navigation:  Gekko commands >

MAP

Previous pageReturn to chapter overviewNext page

A map is a data container, much like a mini-databank, convenient for storage, and for passing variables into and out of functions. It can also be thought of as a named list, where the elements are found (looked up) by means of strings (keys), instead of by consecutive numbers 1, 2, ... etc. Map names always start with the symbol '#', like the other collection types list and matrix. If, for instance, you need to return several variables from a user-defined function, a map is a very convenient container for that purpose.

 

 


 

Syntax

 

#m = ( name = expr, name = expr, ... );     //Definition of a map, where #m is the map name.

MAP ?;                                     //show/count maps in open databanks

 

Refer to the elemements with brackets or dots:

 

#m['x']             //Refer to element with name (key) 'x'. Blanks and other symbols can be used.
#m[x]               //Simple names may omit the single quotes (only when the name does not have type symbols)
#m.x               //Simple names may use dot, in that case type symbols are allowed

 

It is not legal to use for instance MAP m = ... ;, omitting the '#'.

 

For instance, the result of the OLS command could be stored in a list #ols_stats, where #ols_stats[1] could be residual sum of squares, #ols_stats[2] could be standard error, etc. This may get confusing, and a map would allow references to look like #ols_stats['%rss'], #ols_stats['%se'], etc., using expressive names. For simple names, you can also refer to the elements by means of #ols_stats.%rss, #ols_stats.%se, etc. Written like this, it is seen that there is a similarity to databanks, where such a reference to the databank ols_stats would be written as ols_stats:%r2, ols_stats:%se, etc. The similarity is not coincidal: maps really are mini-databanks!

 

 


 

Examples

 

The following example shows the definition of a map #m, consisting of a string and another map, #mm. The map #mm contains a string with the same name, and a timeseries.

 

time 2009 2012;
#= (%i1 = 'a', #mm = (%i1 = 'b', <2010 2011> ts = (1, 2)));
#m.%i1;       //'a', alternatively p #m['%i1'];
#m.#mm.%i1;   //'b', alternatively p #m['#mm']['%i1']
#m.#mm.ts;    //1, 2, alternatively p #m['#mm']['ts']

 

Another example could be the Laspeyres chain index function laspchain(), which returns a map containing an aggregated quantity, and an aggregated price.

 

#= p1, p2, p3;  //or: #p = ('p1', 'p2', 'p3');
#= x1, x2, x3;  //or: #q = ('x1', 'x2', 'x3');
#= laspchain(#p, #q, 2010);

 

Now, #m['q'] and #m['p'], or the shorter #m.q and #m.p refer to the aggregated quantity and price index (as series).

 

 


 

Object orientation

 

Gekko does not yet provide the possibility of defining classes, with class methods etc. that an object derived from a given class use. But the map collection can be used together with the fact that all Gekko functions implement so-called UFCS, so that a function like for instance f(x, y) can generally be written as x.f(y). This makes a kind of poor man's object orientation possible, consider the following example:

 

function val volume(map #m);
  %volume = 0;
  if (#m.%type == 'square');
    %volume = #m.%size * #m.%size;
  else;
    if (#m.%type == 'box');
      %volume = #m.%size * #m.%size * #m.%size;
    end; 
 end;
 return %volume;
end;
 
#sq = (%type = 'square', %size = 2, %color = 'red');
#bx = (%type = 'box', %size = 3, %color = 'green');
tell '';
tell 'Type {#sq.%type}: size = {#sq.%size}, volume = {#sq.volume()}, color = {#sq.%color}';
tell 'Type {#bx.%type}: size = {#bx.%size}, volume = {#bx.volume()}, color = {#bx.%color}';

 

This produces the following output:

 

Type square: size = 2, volume = 4, color = red
Type box: size = 3, volume = 27, color = green

 

Here, map #sq is defined as type 'square', whereas the map #bx is defined as type 'box'. When the volume() function is called on such a map, a square type will return size^2, whereas a box type will return size^3. Therefore, #sq.volume() = 2*2 = 4, whereas #bx.volume() = 3*3*3 = 27. Note that #sq.volume() could alternatively be written volume(#sq), but the variant #sq.volume() has more object method flavor. So in the example, the volume() function called on a square or a box 'knows' how to calculate the volume of that particular object.

 

 


 

Note

 

See the page with syntax diagrams if the basics of names, expressions, etc. is confusing.

 

As it is the case regarding array-series, you may omit the single quotes in indexes, for instance #m[p] will correspond to #m['p']. Please note that this only applies to simple names without symbols, in other words: timeseries names. So do not expect #m[%s] to correspond to #m['%s'] (it does not).

 

Maps are 1-dimensional, more dimensions are not supported regarding names/keys. If you need to handle multidimensional data, you may look into array-series (or matrices).

 

Internally in Gekko, maps are represented in the exact same way as databanks, so in the longer run it is expected that it will be possible to convert seamlessly between maps and databanks.

 

Note that an assignment like #m1 = #m2;, where #m2 is a map, #m1 will become a copy of #m2, not a reference to it. The same is the case regarding function arguments, where manipulating #m1 inside the function body of f(#m1) will not affect #m1 after the function has been left.

 

Regarding variable types and the Gekko type system, see the VAR section. In this appendix, variable assignment rules, including variable types, is explained in more detail.

 

A Gekko map relates to the following in different languages:

 

Gekko: #= (%= 'x', %= 2.2);

R: named list, <- list(a="x", b=2.2)

Python: dict, = {"a": "x", "b": 2.2}

Matlab: struct array or the more general container map.

 

 


 

Related commands

 

LIST