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).