Container

Containers
Description

Containers are a basic form of linked lists. Container functions that you might need are conins, conpoke, conpeek, condel and connull.

A container can store almost any datatype, except of objects. You can store a record/tablebuffer and BinData as a blob inside of a container.

One use of containers is to pass multiple variables between objects using a single call. This is particularly useful when considering 3-tier performance, as each call across a tier can be quite expensive. In that case, putting your variables into one container and passing that in a single call will gain some performance.

Another use is to eliminate some tedious coding practices when writing display methods for report labels. If you have to return values from tables just use a container to store them all then conpeek them out using a global incremental pointer. This pointer can be reset in the executeSection of your Design then use the same method for the DataMethod of your report label something like

display real showValue {   pointer++; return any2real(conpeek(yourcontainer,pointer)); }

Declaration

To declare a container simply prefix the container name with keyword container. static void Job33(Args _args) {   container contest; ; }

Inserting data

I found two methods of inserting data into a container, one is using conins(container, start location, value1,...) same syntax can be used for conpoke(container, start location, value1,...). Using conpoke will actually modify data at that location as opposed to inserting, the idea is that initially you can build your container with conpoke but if your container will need data added then use conins which will insert the data at specified location and bump the rest of the data.

static void Job33(Args _args) {   container contest; ;

contest =conpoke(contest,1,"bla",3); //contest = conins(contest,1,"bla",3);

print conpeek(contest,2);

pause; }

In the above I use conpoke to insert two values starting at location 1 into contest. So after this our container will ... contain :) value "bla" at location 1 and value 3 at location 2. Data type for values is anytype. conpeek returns anytype data type.

Appending data

The normal way to append data to variable is // e.g. using a string testString = testString + 'append data'

// e.g. using a container contest = contest + ['next value']; A problem for this operation with large containers will discussed in the next chapter.

The best way for appending data to a container is: // use this way to append data contest += ['next value']; // instead of this contest = contest + ['next value'] The += is much faster and needs less memory than the normal way...

Compare

Its also possible to compare 2 containers with each other using the normal operators ==, >, >=, <, <=, !=. Comparing containers results in comparing the content, the first element of container 1 will be compared with the first element of container 2, and so on..... until the comparision gets a result!

Display a container

for displaying the content and structure of a container, use the static-method conview of the class global. conview creates a formRun-Object showing the container as a tree. This formRun can be shown as a normal form or it can be used as lookup-form. static void testContainer(Args _args) {   container testContainer1,testContainer2; ;   testContainer1 = [ 'Hello World', today, DocumentStatus::Invoice ]; testContainer2 = [ CustTable::find('4004'), testContainer1, 'blabla' );

conview(testContainer1); global::conview(testContainer2); }

Performance considerations
One important detail about containers is the fact, that they are static structures. You cannot modify a container in-place, instead each addition or deletion has to iterate over the entire structure to copy all values into a newly allocated one. So every container manipulation has a runtime of O(n). Thus, whenever possible, read and write containers as a batch:

// writing a container packedClass = [ version, var1, var2, var3 ];

// reading a container [ version, var1, var2, var3 ] = packedClass;

I cannot tell how this is for reading operations, I suspect that they're O(1) much like arrays, but I wouldn't bet anything on it.

This whole thing is especially tricky in cases where you need variable-length containers which you usually create dynamically: This actually scales rather bad. However, there is a rather simple trick you can use if you run into this problem: The integrated List class does have a very nice packed format, which is just about what you need:

static container List2Container(List list) {   container result;

result = list.pack; switch (conpeek(result, 1)) {       case 1: return condel(result, 1, 3);

default: throw error(strfmt("The List version %1 is not suppported.", conpeek(result, 1))); } }

I had performance problems in this respect with containers having 50+ elements which have to be assembled dynamically (for various reasons not important here). Changing to a list and converting it into a container in a single batch is at least one magnitude faster there (no wonder, as appending to a list works in O(1) rather then O(n)).