github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/docs/commands/foreach.md (about)

     1  # `foreach`
     2  
     3  > Iterate through an array
     4  
     5  ## Description
     6  
     7  `foreach` reads an array or map from STDIN and iterates through it, running
     8  a code block for each iteration with the value of the iterated element passed
     9  to it.
    10  
    11  By default `foreach`'s output data type is inherited from its input data type.
    12  For example is STDIN is `yaml` then so will STDOUT. The only exception to this
    13  is if STDIN is `json` in which case STDOUT will be jsonlines (`jsonl`), or when
    14  additional flags are used such as `--jmap`.
    15  
    16  ## Usage
    17  
    18  `{ code-block }` reads from a variable and writes to an array / unbuffered STDOUT:
    19  
    20  ```
    21  <stdin> -> foreach variable { code-block } -> <stdout>
    22  ```
    23  
    24  `{ code-block }` reads from STDIN and writes to an array / unbuffered STDOUT:
    25  
    26  ```
    27  <stdin> -> foreach { -> code-block } -> <stdout>
    28  ```
    29  
    30  `foreach` writes to a buffered JSON map:
    31  
    32  ```
    33  <stdin> -> foreach --jmap variable {
    34      code-block (map key)
    35  } {
    36      code-block (map value)
    37  } -> <stdout>
    38  ```
    39  
    40  ## Examples
    41  
    42  There are two basic ways you can write a `foreach` loop depending on how you
    43  want the iterated element passed to the code block.
    44  
    45  The first option is to specify a temporary variable which can be read by the
    46  code block:
    47  
    48  ```
    49  » a [1..3] -> foreach i { out $i }
    50  1
    51  2
    52  3
    53  ```
    54  
    55  > Please note that the variable is specified **without** the dollar prefix,
    56  > then used in the code block **with** the dollar prefix.
    57  
    58  The second option is for the code block's STDIN to read the element:
    59  
    60  ```
    61  » a [1..3] -> foreach { -> cat }
    62  1
    63  2
    64  3
    65  ```
    66  
    67  > STDIN can only be read as the first command. If you cannot process the
    68  > element on the first command then it is recommended you use the first
    69  > option (passing a variable) instead.
    70  
    71  ### Writing JSON maps
    72  
    73  ```
    74  » ja [Monday..Friday] -> foreach --jmap day { out $day -> left 3 } { $day }
    75  {
    76      "Fri": "Friday",
    77      "Mon": "Monday",
    78      "Thu": "Thursday",
    79      "Tue": "Tuesday",
    80      "Wed": "Wednesday"
    81  } 
    82  ```
    83  
    84  ### Using steps to jump iterations by more than 1 (one)
    85  
    86  You can step through an array, list or table in jumps of user definable
    87  quantities. The value passed in STDIN and $VAR will be an array of all
    88  the records within that step range. For example:
    89  
    90  ```
    91  » %[1..10] -> foreach --step 3 value { out "Iteration $.i: $value" }
    92  Iteration 1: [
    93      1,
    94      2,
    95      3
    96  ]
    97  Iteration 2: [
    98      4,
    99      5,
   100      6
   101  ]
   102  Iteration 3: [
   103      7,
   104      8,
   105      9
   106  ]
   107  Iteration 4: [
   108      10
   109  ]
   110  ```
   111  
   112  ## Flags
   113  
   114  * `--jmap`
   115      Write a `json` map to STDOUT instead of an array
   116  * `--step`
   117      `<int>` Iterates in steps. Value passed to block is an array of items in the step range. Not (yet) supported with `--jmap`
   118  
   119  ## Detail
   120  
   121  ### Meta values
   122  
   123  Meta values are a JSON object stored as the variable `$.`. The meta variable
   124  will get overwritten by any other block which invokes meta values. So if you
   125  wish to persist meta values across blocks you will need to reassign `$.`, eg
   126  
   127  ```
   128  %[1..3] -> foreach {
   129      meta_parent = $.
   130      %[7..9] -> foreach {
   131          out "$(meta_parent.i): $.i"
   132      }
   133  }
   134  ```
   135  
   136  The following meta values are defined:
   137  
   138  * `i`: iteration number
   139  
   140  ### Preserving the data type (when no flags used)
   141  
   142  `foreach` will preserve the data type read from STDIN in all instances where
   143  data is being passed along the pipeline and push that data type out at the
   144  other end:
   145  
   146  * The temporary variable will be created with the same data-type as
   147    `foreach`'s STDIN, or the data type of the array element (eg if it is a
   148    string or number)
   149  * The code block's STDIN will have the same data-type as `foreach`'s STDIN
   150  * `foreeach`'s STDOUT will also be the same data-type as it's STDIN (or `jsonl`
   151    (jsonlines) where STDIN was `json` because `jsonl` better supports streaming)
   152  
   153  This last point means you may need to `cast` your data if you're writing
   154  data in a different format. For example the following is creating a YAML list
   155  however the data-type is defined as `json`:
   156  
   157  ```
   158  » ja [1..3] -> foreach i { out "- $i" }
   159  - 1
   160  - 2
   161  - 3
   162  
   163  » ja [1..3] -> foreach i { out "- $i" } -> debug -> [[ /Data-Type/Murex ]]
   164  json
   165  ```
   166  
   167  Thus any marshalling or other data-type-aware API's would fail because they
   168  are expecting `json` and receiving an incompatible data format.
   169  
   170  This can be resolved via `cast`:
   171  
   172  ```
   173  » ja [1..3] -> foreach i { out "- $i" } -> cast yaml
   174  - 1
   175  - 2
   176  - 3
   177  
   178  » ja [1..3] -> foreach i { out "- $i" } -> cast yaml -> debug -> [[ /Data-Type/Murex ]]
   179  yaml
   180  ```
   181  
   182  The output is the same but now it's defined as `yaml` so any further pipelined
   183  processes will now automatically use YAML marshallers when reading that data.
   184  
   185  ### Tips when writing JSON inside for loops
   186  
   187  One of the drawbacks (or maybe advantages, depending on your perspective) of
   188  JSON is that parsers generally expect a complete file for processing in that
   189  the JSON specification requires closing tags for every opening tag. This means
   190  it's not always suitable for streaming. For example
   191  
   192  ```
   193  » ja [1..3] -> foreach i { out ({ "$i": $i }) }
   194  { "1": 1 }
   195  { "2": 2 }
   196  { "3": 3 }
   197  ```
   198  
   199  **What does this even mean and how can you build a JSON file up sequentially?**
   200  
   201  One answer if to write the output in a streaming file format and convert back
   202  to JSON
   203  
   204  ```
   205  » ja [1..3] -> foreach i { out (- "$i": $i) }
   206  - "1": 1
   207  - "2": 2
   208  - "3": 3
   209  
   210  » ja [1..3] -> foreach i { out (- "$i": $i) } -> cast yaml -> format json
   211  [
   212      {
   213          "1": 1
   214      },
   215      {
   216          "2": 2
   217      },
   218      {
   219          "3": 3
   220      }
   221  ]
   222  ```
   223  
   224  **What if I'm returning an object rather than writing one?**
   225  
   226  The problem with building JSON structures from existing structures is that you
   227  can quickly end up with invalid JSON due to the specifications strict use of
   228  commas.
   229  
   230  For example in the code below, each item block is it's own object and there are
   231  no `[ ... ]` encapsulating them to denote it is an array of objects, nor are
   232  the objects terminated by a comma.
   233  
   234  ```
   235  » config -> [ shell ] -> formap k v { $v -> alter /Foo Bar }
   236  {
   237      "Data-Type": "bool",
   238      "Default": true,
   239      "Description": "Display the interactive shell's hint text helper. Please note, even when this is disabled, it will still appear when used for regexp searches and other readline-specific functions",
   240      "Dynamic": false,
   241      "Foo": "Bar",
   242      "Global": true,
   243      "Value": true
   244  }
   245  {
   246      "Data-Type": "block",
   247      "Default": "{ progress $PID }",
   248      "Description": "Murex function to execute when an `exec` process is stopped",
   249      "Dynamic": false,
   250      "Foo": "Bar",
   251      "Global": true,
   252      "Value": "{ progress $PID }"
   253  }
   254  {
   255      "Data-Type": "bool",
   256      "Default": true,
   257      "Description": "ANSI escape sequences in Murex builtins to highlight syntax errors, history completions, {SGR} variables, etc",
   258      "Dynamic": false,
   259      "Foo": "Bar",
   260      "Global": true,
   261      "Value": true
   262  }
   263  ...
   264  ```
   265  
   266  Luckily JSON also has it's own streaming format: JSON lines (`jsonl`). We can
   267  `cast` this output as `jsonl` then `format` it back into valid JSON:
   268  
   269  ```
   270  » config -> [ shell ] -> formap k v { $v -> alter /Foo Bar } -> cast jsonl -> format json
   271  [
   272      {
   273          "Data-Type": "bool",
   274          "Default": true,
   275          "Description": "Write shell history (interactive shell) to disk",
   276          "Dynamic": false,
   277          "Foo": "Bar",
   278          "Global": true,
   279          "Value": true
   280      },
   281      {
   282          "Data-Type": "int",
   283          "Default": 4,
   284          "Description": "Maximum number of lines with auto-completion suggestions to display",
   285          "Dynamic": false,
   286          "Foo": "Bar",
   287          "Global": true,
   288          "Value": "6"
   289      },
   290      {
   291          "Data-Type": "bool",
   292          "Default": true,
   293          "Description": "Display some status information about the stop process when ctrl+z is pressed (conceptually similar to ctrl+t / SIGINFO on some BSDs)",
   294          "Dynamic": false,
   295          "Foo": "Bar",
   296          "Global": true,
   297          "Value": true
   298      },
   299  ...
   300  ```
   301  
   302  #### `foreach` will automatically cast it's output as `jsonl` _if_ it's STDIN type is `json`
   303  
   304  ```
   305  » ja [Tom,Dick,Sally] -> foreach name { out Hello $name }
   306  Hello Tom
   307  Hello Dick
   308  Hello Sally
   309  
   310  » ja [Tom,Dick,Sally] -> foreach name { out Hello $name } -> debug -> [[ /Data-Type/Murex ]]
   311  jsonl
   312  
   313  » ja [Tom,Dick,Sally] -> foreach name { out Hello $name } -> format json
   314  [
   315      "Hello Tom",
   316      "Hello Dick",
   317      "Hello Sally"
   318  ]
   319  ```
   320  
   321  ## See Also
   322  
   323  * [`ReadArrayWithType()` (type)](../apis/ReadArrayWithType.md):
   324    Read from a data type one array element at a time and return the elements contents and data type
   325  * [`[[ Element ]]`](../parser/element.md):
   326    Outputs an element from a nested structure
   327  * [`a` (mkarray)](../commands/a.md):
   328    A sophisticated yet simple way to build an array or list
   329  * [`break`](../commands/break.md):
   330    Terminate execution of a block within your processes scope
   331  * [`cast`](../commands/cast.md):
   332    Alters the data type of the previous function without altering it's output
   333  * [`debug`](../commands/debug.md):
   334    Debugging information
   335  * [`for`](../commands/for.md):
   336    A more familiar iteration loop to existing developers
   337  * [`formap`](../commands/formap.md):
   338    Iterate through a map or other collection of data
   339  * [`format`](../commands/format.md):
   340    Reformat one data-type into another data-type
   341  * [`if`](../commands/if.md):
   342    Conditional statement to execute different blocks of code depending on the result of the condition
   343  * [`ja` (mkarray)](../commands/ja.md):
   344    A sophisticated yet simply way to build a JSON array
   345  * [`json`](../types/json.md):
   346    JavaScript Object Notation (JSON)
   347  * [`jsonl`](../types/jsonl.md):
   348    JSON Lines
   349  * [`left`](../commands/left.md):
   350    Left substring every item in a list
   351  * [`out`](../commands/out.md):
   352    Print a string to the STDOUT with a trailing new line character
   353  * [`while`](../commands/while.md):
   354    Loop until condition false
   355  * [`yaml`](../types/yaml.md):
   356    YAML Ain't Markup Language (YAML)
   357  
   358  <hr/>
   359  
   360  This document was generated from [builtins/core/structs/foreach_doc.yaml](https://github.com/lmorg/murex/blob/master/builtins/core/structs/foreach_doc.yaml).