Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
manipulating_tables_and_lookup_tables [2026/08/03 02:05] hermann |
manipulating_tables_and_lookup_tables [2026/08/18 03:09] (current) hermann [Printing a table with any number of key columns] |
||
|---|---|---|---|
| Line 585: | Line 585: | ||
| }}; | }}; | ||
| }}; | }}; | ||
| - | |||
| - | // priceTable is usable here, once the Group it was produced inside -- and | ||
| - | // through its sequenceInput, the loop that Group was forced to wait for -- | ||
| - | // has finished. | ||
| </code> | </code> | ||
| + | |||
| + | ''priceTable'' is usable here, once the Group it was produced inside — and through its ''sequenceInput'', the loop that Group was forced to wait for — has finished. | ||
| The loop's own iterations are still eligible to run in parallel with each other: nothing inside it is a Mux, nothing inside it is consumed from outside it, and it feeds no submodel output port -- see [[basic_data_flow|Basic Data Flow]]. Only the loop as a whole is sequenced against the merge, which is why this avoids the cost the Mux-accumulator pattern above pays on every single iteration. | The loop's own iterations are still eligible to run in parallel with each other: nothing inside it is a Mux, nothing inside it is consumed from outside it, and it feeds no submodel output port -- see [[basic_data_flow|Basic Data Flow]]. Only the loop as a whole is sequenced against the merge, which is why this avoids the cost the Mux-accumulator pattern above pays on every single iteration. | ||
| Line 595: | Line 593: | ||
| > **Note:** ''year'' is passed to ''SubTable'' directly, not wrapped in brackets as ''[year]''. Bracket syntax is for Tuple **literals**; a connected Real value converts to a one-element Tuple automatically on connection, the same pattern used by ''GetTableFromKey'' in [[#sub-tables|Sub-Tables]] above. | > **Note:** ''year'' is passed to ''SubTable'' directly, not wrapped in brackets as ''[year]''. Bracket syntax is for Tuple **literals**; a connected Real value converts to a one-element Tuple automatically on connection, the same pattern used by ''GetTableFromKey'' in [[#sub-tables|Sub-Tables]] above. | ||
| - | There is no functor exposing ''TableManager'''s ''eraseSubTables'' or ''getTableCount'' methods -- those are internal bookkeeping, not part of the functor catalog. | ||
| ===== Printing a table with a generic format ===== | ===== Printing a table with a generic format ===== | ||
| Line 603: | Line 600: | ||
| [[Get Table Row]], [[Get Tuple Size]], and [[Get Tuple Value]] make the column-count side of this possible: a row read as a Tuple can be measured with ''GetTupleSize'', and each of its elements retrieved by position, rather than by column name, with ''GetTupleValue''. Each retrieved element has type ''TableValue'' — a generic type able to hold either a Real or a String. | [[Get Table Row]], [[Get Tuple Size]], and [[Get Tuple Value]] make the column-count side of this possible: a row read as a Tuple can be measured with ''GetTupleSize'', and each of its elements retrieved by position, rather than by column name, with ''GetTupleValue''. Each retrieved element has type ''TableValue'' — a generic type able to hold either a Real or a String. | ||
| - | [[String]] and [[Real Value]] each convert a ''TableValue'' the same conditional way: the conversion only succeeds if the ''TableValue'' actually holds that type, and raises an error otherwise — converting an already-typed, known ''RealValue'' or String, by contrast, never fails. Attempting the String conversion wrapped in [[Skip On Error]] turns that failure into a type test: ''SkipOnError'' catches the error rather than aborting the model, and its ''executionCompletedSucessfully'' output reports whether the ''TableValue'' really was a String. [[If Then]] and [[If Not Then]] branch on that result and take the matching path. The first uses the converted String directly. The second has already ruled String out, so it extracts a ''RealValue'' instead — safe now, since only Real remains possible — and converts //that// known value to a string. A [[String Junction]] then recombines the two branches into one result, since only one of them ever actually runs. | + | [[String]] and [[Real Value]] each convert a ''TableValue'' the same conditional way: the conversion only succeeds if the ''TableValue'' actually holds that type, and raises an error otherwise — converting an already-typed, known ''RealValue'' or String, by contrast, never fails. Wrapping each attempt in its own [[Skip On Error]] turns that failure into "produced nothing" instead of aborting the model, so only the attempt that actually matches the ''TableValue'''s real type ever produces a display string; the other attempt simply fails silently and produces nothing. A [[String Junction]] then picks whichever one exists. |
| + | |||
| + | > **Note:** A junction always tries its first port before its second — see [[ego_script#carrying_and_selecting_values_across_iterations|Carrying and selecting values across iterations]] for this rule stated generally, since it applies identically to every Junction functor (''String'', ''Table'', ''Value'', ''Lookup Table'', ''Map'', ''Categorical Map'', ''Folder''). It makes no practical difference here, though: the two attempts below are mutually exclusive by construction, so only one of ''stringDisplay''/''realDisplay'' is ever available for the junction to pick regardless of port order. | ||
| <code> | <code> | ||
| Line 629: | Line 628: | ||
| displayColumn := $ [ $columnIndex - 1 ]; | displayColumn := $ [ $columnIndex - 1 ]; | ||
| - | // Attempt to read the cell as a String; SkipOnError catches the | + | // Try the cell as a String; SkipOnError leaves stringDisplay |
| - | // failure if it's actually a Real, rather than aborting. | + | // unproduced if cellValue is actually a Real, rather than |
| - | attempt := SkipOnError .yes {{ | + | // aborting the model. |
| - | asString := String cellValue; | + | _ := SkipOnError .yes {{ |
| - | }}; | + | stringDisplay := String cellValue; |
| - | + | ||
| - | _ := IfThen attempt {{ | + | |
| - | stringDisplay := String asString; | + | |
| }}; | }}; | ||
| - | _ := IfNotThen attempt {{ | + | // Try the cell as a Real instead; SkipOnError leaves |
| - | // cellValue was a Real instead; RealValue extracts it, and | + | // realDisplay unproduced if cellValue is actually a String. |
| - | // String — safe here, since converting a known Real never | + | _ := SkipOnError .yes {{ |
| - | // fails — turns it into a display string. | + | |
| asReal := RealValue cellValue; | asReal := RealValue cellValue; | ||
| realDisplay := String asReal; | realDisplay := String asReal; | ||
| }}; | }}; | ||
| - | // Only one of stringDisplay/realDisplay was actually produced; | + | // Exactly one of the two attempts above ever succeeds; |
| - | // StringJunction picks whichever one has a value. | + | // StringJunction picks whichever one was produced. |
| cellDisplay := StringJunction stringDisplay realDisplay; | cellDisplay := StringJunction stringDisplay realDisplay; | ||
| Line 1050: | Line 1045: | ||
| ]; | ]; | ||
| - | result := CalculatePythonExpression (String $"( | + | result := CalculatePythonExpression $"( |
| inputTable = dinamica.inputs['t1'] | inputTable = dinamica.inputs['t1'] | ||
| header = inputTable[0] | header = inputTable[0] | ||
| Line 1063: | Line 1058: | ||
| dinamica.outputs['reshapedTable'] = dinamica.prepareTable(newTable, 1) | dinamica.outputs['reshapedTable'] = dinamica.prepareTable(newTable, 1) | ||
| - | )") {{ | + | )" {{ |
| NumberTable myTable 1; | NumberTable myTable 1; | ||
| }}; | }}; | ||
| Line 1085: | Line 1080: | ||
| // Same generic Real/String printing technique as the earlier | // Same generic Real/String printing technique as the earlier | ||
| - | // example: attempt String first, SkipOnError catches the | + | // example: try both conversions independently, each guarded by |
| - | // failure if it's actually a Real, RealValue then converts it. | + | // its own SkipOnError, and let StringJunction pick whichever |
| - | attempt := SkipOnError .yes {{ | + | // one was actually produced. |
| - | asString := String cellValue; | + | _ := SkipOnError .yes {{ |
| + | stringDisplay := String cellValue; | ||
| }}; | }}; | ||
| - | _ := IfThen attempt {{ | + | _ := SkipOnError .yes {{ |
| - | stringDisplay := String asString; | + | |
| - | }}; | + | |
| - | + | ||
| - | _ := IfNotThen attempt {{ | + | |
| asReal := RealValue cellValue; | asReal := RealValue cellValue; | ||
| realDisplay := String asReal; | realDisplay := String asReal; | ||
| Line 1121: | Line 1113: | ||
| Reach for a general Table instead when a single Real value per Real key isn't enough — when a row needs more than one associated value, or a String value, or a String key, or when rows need to be indexed by more than one key at once. A String key column costs more than the type change alone: iterating one means mapping each placeholder index back to its actual key inside the loop, so prefer a Real key column wherever the data allows. | Reach for a general Table instead when a single Real value per Real key isn't enough — when a row needs more than one associated value, or a String value, or a String key, or when rows need to be indexed by more than one key at once. A String key column costs more than the type change alone: iterating one means mapping each placeholder index back to its actual key inside the loop, so prefer a Real key column wherever the data allows. | ||
| - | |||