This is an old revision of the document!


PHP's gd library is missing or unable to create PNG images

Manipulating Tables and Lookup Tables

Overview

This page covers the functors and techniques used to read, iterate over, and restructure Tables and Lookup Tables once they exist. See Table Type and Lookup Table Type for the general format of these two types — column naming, key marking, and type inference — which this page assumes as background.

Tuple Type

A Tuple is a sequence of table cells, most often used to represent the key (or full set of keys) identifying a table row. Tuple elements are ordered by column index and have no names of their own — a Tuple by itself doesn't say which column each element belongs to; that mapping only exists in the context of the table it's being used against.

A single Real or String value connects directly to any port expecting a Tuple — Dinamica EGO converts it automatically, which is why a plain key value can be wired straight into a functor like GetTableValue without explicitly constructing a Tuple first. A multi-column key has to be built explicitly, one element at a time, by chaining AddTupleValue calls — each call appends one value to the Tuple produced by the previous call.

Every functor below that identifies a row or sub-table by key takes that key as a Tuple.

Retrieving values and rows

Functor Inputs Output Notes
GetTableValue table, keys (Tuple), column (name or index), optional valueIfNotFound The value at that key/column Reads a single cell.
GetTableRow keys (Tuple), table The full row, as a Tuple Reads every value column for one key.
GetLookupTableValue table (Lookup Table), key, optional valueIfNotFound The value for that key The Lookup Table equivalent of GetTableValue — no column argument, since a Lookup Table only ever has one.

All three accept an optional fallback value returned when the key isn't present, instead of failing.

Iterating over Lookup Table values

A For Each loop run over a connected Lookup Table executes once per key. Inside the loop, a Step functor retrieves the current key — its input auto-binds to the container's current iteration value, the same mechanism described in Internal output ports, so it needs no explicit wiring. From there, the key can be used directly, or passed to GetLookupTableValue to retrieve the corresponding value.

A loop like this can often run its iterations in parallel — see Basic Data Flow for the conditions that allow it.

Iterating over Table rows

Table rows are iterated the same way, but need one extra functor: GetTableKeys returns a table mapping unique indices to the keys of the input table's first key column. When the key column is already Real-typed, those indices and the keys are the same numbers, so the mapping is trivial. When the key column is String-typed, the indices are distinct numeric placeholders, and a GetTableValue call inside the loop is needed to map the current index back to its actual String key — this indirection is why String-typed key columns are usually avoided when a table will be iterated (see Table Type).

A table with more than one key column can only be iterated one key column at a time this way. To examine every key column, nest loops — see Sub-Tables below.

Sub-Tables

A sub-table is a table containing only the rows matching one specific value of a key, with that key column removed from the result. Sub-tables are how Dinamica handles tables with more than one key column: peel off one key at a time, working with what remains.

Functor Inputs Output Notes
GetTableFromKey table, keys (Tuple) The matching sub-table Retrieves rows for the given key(s), with those key columns dropped.
SetTableByKey table, keys (Tuple), subTable The updated table Inserts or replaces the rows for the given key(s). The sub-table's column names and types must match the target table.

Both functors only operate on the table's leftmost key column(s) — they can't reach into the middle of a composite key. If the key you need isn't leftmost, reorder the columns first with ReorderTableColumn, which moves one column (key or value) to a new index; key and value columns can't be moved across each other.

Worked example. Take a table of commodity prices, keyed by Year and City:

Year* City* Price
2004 4534272 1200
2004 5483552 1453
2007 4534272 4332
2007 5483552 233

Retrieving the sub-table for Year 2007 — a single key, since Year is leftmost — leaves City/Price pairs:

City* Price
4534272 4332
5483552 233

To instead retrieve by City first, Year would need to be reordered ahead of it, since GetTableFromKey can only key off the leftmost column(s). With three key columns, the same idea nests: peel off the leftmost key to get a sub-table, then peel off the next leftmost key of that sub-table, and so on — which is exactly how nested ForEach loops iterate a multi-key table one column at a time; see Container functors for how nesting containers this way affects execution order.

Choosing between Tables and Lookup Tables

A Lookup Table's fixed shape — one Real key, one Real value — makes it faster to query and iterate than a general Table, and it supports proximity-based lookups (nearest key, linear interpolation) that Tables don't. Prefer a Lookup Table whenever the data actually fits that shape, including inside map/value expressions, where Lookup Table operands add less evaluation overhead than Table operands — see Lookup Table Operators and Multi-Column Table Operators for how each is queried from within an expression.

Reach for a general Table instead when a single value per key isn't enough — when a row needs more than one associated value, or a String value, or when rows need to be indexed by more than one key at once.

Examples

Both examples below use Print, Step, and CreateString to log each entry to the message console. Print is itself a container — its initialMessage is printed before whatever it contains runs, so an empty {{ }} block is enough when the only goal is to print something. CreateString builds a message from a format string and the values connected inside it, referenced the same way a Code expression references a hook — by a numbered, type-prefixed tag (<v1> for the first connected value, and so on). Both examples also use { initialMessage = message } nominal syntax for Print, and the _ discard convention for outputs that aren't needed.

Printing every entry of a Lookup Table:

_ := ForEach myLookupTable {{
    key := Step;
    value := GetLookupTableValue myLookupTable key;

    message := CreateString "(Key: <v1>, Value: <v2>)" {{
        NumberValue key 1;
        NumberValue value 2;
    }};

    _ := Print { initialMessage = message } {{ }};
}};

Printing every entry of a Table (with a Real-typed key column and a value column named Population):

allKeys := GetTableKeys myTable;

_ := ForEach allKeys {{
    key := Step;
    value := GetTableValue myTable key "Population";

    message := CreateString "(Key: <v1>, Value: <v2>)" {{
        NumberValue key 1;
        NumberValue value 2;
    }};

    _ := Print { initialMessage = message } {{ }};
}};

If myTable's key column were String-typed instead of Real, allKeys would hold numeric placeholder indices rather than the real keys, and an extra GetTableValue call would be needed inside the loop to map each index back to its actual key — see Iterating over Table rows above.