Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
manipulating_tables_and_lookup_tables [2026/07/25 03:50]
hermann
manipulating_tables_and_lookup_tables [2026/07/25 04:29] (current)
hermann
Line 162: Line 162:
         NumberString key 1;         NumberString key 1;
         NumberValue value 1;         NumberValue value 1;
 +    }};
 +
 +    _ := Print { initialMessage = message } {{ }};
 +}};
 +</​code>​
 +
 +**Example** — a table with more than one value column. Nothing new is needed: call ''​GetTableValue''​ once per value column and combine the results:
 +
 +<​code>​
 +myTable := Table [
 +    "​CityId*",​ "​Population",​ "​Area",​
 +    1, 667137, 125,
 +    2, 39398, 6
 +];
 +
 +allKeys := GetTableKeys myTable;
 +
 +_ := ForEach allKeys {{
 +    key := Step;
 +    population := GetTableValue myTable key "​Population";​
 +    area := GetTableValue myTable key "​Area";​
 +
 +    message := CreateString "​(CityId:​ <v1>, Population: <v2>, Area: <​v3>​)"​ {{
 +        NumberValue key 1;
 +        NumberValue population 2;
 +        NumberValue area 3;
     }};     }};
  
Line 236: Line 262:
 // citySubTable will be the Year*/Price pairs for city 4534272. // citySubTable will be the Year*/Price pairs for city 4534272.
 citySubTable := GetTableFromKey priceTableByCity 4534272; citySubTable := GetTableFromKey priceTableByCity 4534272;
 +</​code>​
 +
 +**Example** — printing every entry of a table with more than one key column. [[Get Table Keys]] only ever iterates the //first// key column, so a table with several needs one nested loop per extra key: peel off a sub-table for each ''​Year'',​ then iterate the ''​City''​ key within it:
 +
 +<​code>​
 +priceTable := Table [
 +    "​Year*",​ "​City*",​ "​Price",​
 +    2004, 4534272, 1200,
 +    2004, 5483552, 1453,
 +    2007, 4534272, 4332,
 +    2007, 5483552, 233
 +];
 +
 +yearKeys := GetTableKeys priceTable;
 +
 +_ := ForEach yearKeys {{
 +    year := Step;
 +    pricesInYear := GetTableFromKey priceTable year;
 +
 +    cityKeys := GetTableKeys pricesInYear;​
 +
 +    _ := ForEach cityKeys {{
 +        city := Step;
 +        price := GetTableValue pricesInYear city "​Price";​
 +
 +        message := CreateString "​(Year:​ <v1>, City: <v2>, Price: <​v3>​)"​ {{
 +            NumberValue year 1;
 +            NumberValue city 2;
 +            NumberValue price 3;
 +        }};
 +
 +        _ := Print { initialMessage = message } {{ }};
 +    }};
 +}};
 +</​code>​
 +
 +===== Storing results across a loop =====
 +
 +A [[Mux Table]] or [[Mux Lookup Table]] can carry a Table or Lookup Table across the iterations of a loop, the same way [[Mux Value]] carries a single value — see [[ego_script#​carrying_and_selecting_values_across_iterations|Carrying and selecting values across iterations]]. Each iteration reads the mux's current output, adds a row to it with ''​AddTableRow'',​ and feeds the result back in as the mux's ''​feedback''​ input for the next iteration, building up a result table one row at a time.
 +
 +The accumulated table is read after the loop the same way any container'​s internal result is read from outside it: a functor after the loop simply takes the accumulator'​s feedback variable as an input. There'​s nothing special about this — the loop is guaranteed to finish before anything depending on it runs, per [[basic_data_flow|Basic Data Flow]].
 +
 +<​code>​
 +sourceLookup := LookupTable [
 +    "​Key"​ "​Value",​
 +    1 667137,
 +    2 39398,
 +    3 181045
 +];
 +
 +emptyResults := Table [
 +    "​CityId*#​real",​ "​DoubledPopulation#​real"​
 +];
 +
 +_ := ForEach sourceLookup {{
 +    accumulated := MuxTable emptyResults nextAccumulated;​
 +
 +    key := Step;
 +    population := GetLookupTableValue sourceLookup key;
 +    doubled := $ [ $population * 2 ];
 +
 +    newRow := AddTupleValue key doubled;
 +    nextAccumulated := AddTableRow accumulated newRow;
 +}};
 +
 +// finalResults holds every row added across all iterations. Use nextAccumulated,​
 +// not accumulated (the mux's own output), which lags one iteration behind.
 +// The Table carrier passes it through, since := always binds a functor call.
 +finalResults := Table nextAccumulated;​
 +</​code>​
 +
 +> **Note:** This is also why ''​accumulated''​ should never be read again after ''​AddTableRow''​ consumes it in the same iteration. When exactly one reference to a table'​s current version remains, Dinamica can update it destructively — modifying the existing storage in place. A second live reference to ''​accumulated''​ (reading it directly somewhere else, instead of only through ''​nextAccumulated''​) forces Dinamica to copy the table instead, since the old version now has to stay intact for that other reference. That copy cost repeats every iteration — see [[basic_data_flow|Basic Data Flow]] for this same rule stated generally, beyond just tables.
 +
 +**Why this isn't the best approach.** [[basic_data_flow|Basic Data Flow]] documents three conditions under which a loop's iterations can run simultaneously:​ no mux directly inside the loop, nothing produced inside the loop consumed outside it, and no loop member used as a submodel'​s output port. A mux-based accumulator like the one above breaks the first condition outright — the mux ties every iteration to the one before it, so the loop can never run as anything but sequential, even when the per-iteration work is otherwise completely independent,​ as it is above (each row's value depends only on that row's own key).
 +
 +When the transformation is this simple — one output row per input row, computed independently — the calculator shorthand already covered in [[calculate_functors#​5_lookup_table_operators|Lookup Table Operators]] produces the same result without a loop, a mux, or the sequential cost that comes with one:
 +
 +<​code>​
 +doubledResults := % [ %sourceLookup[line] * 2 ] "​CityId"​ "​DoubledPopulation"​ sourceLookup;​
 </​code>​ </​code>​