each

each, keyword

The each keyword offers an iteration mechanism against an observation table. Iterations are not ordered.

table Obs = with
  [| as N |]
  [| 1 |]
  [| 2 |]
  [| 3 |]

Obs.Cpy = each Obs
  cpy = Obs.N + 1
  return cpy

show table "" with Obs.N, Obs.Cpy

each .. scan , keyword

The each .. scan keywords offer an iteration mechanism against an observation table. Iterations are ordered based on the argument passed to scan. The scan expression must depend only on data available before the loop starts. Expressions computed inside the loop are rejected to avoid rebuilding indices per iteration.

table Obs = with
  [| date(2021, 1, 1) as Date, 13 as Quantity |]
  [| date(2021, 2, 1)       ,  11             |]
  [| date(2021, 3, 1)       ,  17             |]
  [| date(2021, 4, 1)       ,  18             |]
  [| date(2021, 5, 1)       ,  16             |]

Best = 0

Obs.BestSoFar = each Obs scan Obs.Date
  keep Best
  NewBest = max(Best, Obs.Quantity)
  Best = NewBest
  return NewBest

show table "" with Obs.Date, Obs.BestSoFar

Table diagram

each uses the table diagram to decide how tables are accessed in the loop:

Reads from upstream tables return the scalar value matching the current iteration line. Writes to the iteration table or upstream tables are forbidden; use keep Upstream.X as X to update upstream values through a scalar alias. Writing to upstream-cross tables is forbidden. Broadcasting into the iteration table or upstream tables is forbidden.

The keep statements must appear first in the block and refer to variables defined before the loop. each .. scan requires at least one keep. desc reverses the scan order.

Upstream vectors must be kept as scalars and updated through an alias:

table Items[id] = with
  [| as Id, as X, as Cat |]
  [| 1, 1, "A" |]
  [| 2, 2, "B" |]

table Categories[cat] = by Items.Cat
Categories.X = 0

Items.B = each Items scan Items.Cat
  keep Categories.X as X
  X = Items.X + X
  return X

Return-less blocks

If only the final keep values matter, return can be omitted:

table Currencies = with
  [| as Code |]
  [| "EUR"   |]
  [| "JPY"   |]
  [| "USD"   |]

Sep = ""
List = ""
each Currencies scan Currencies.Code
  keep Sep
  keep List
  List = "\{List}\{Sep}\{Currencies.Code}"
  Sep = ", "

show scalar "" with List

auto ordering

scan auto follows the primary dimension ordering:

table T = extend.range(3)
x = 0
T.X = each T scan auto
  keep x
  x = T.N - x
  return x

Any-order blocks

scan T.* allows arbitrary ordering:

table Obs = with
  [| as X |]
  [| 42   |]
  [| 41   |]
  [| 45   |]

myMin = 1B
myMax = -(1B)
each Obs scan Obs.*
  keep myMin
  keep myMax
  myMin = min(myMin, Obs.X)
  myMax = max(myMax, Obs.X)

each .. when

when filters iterations and is only allowed on return-less blocks:

table T = extend.range(5)
s = 0
each T scan auto when T.N mod 2 == 1
  keep s
  s = s + T.N

Reasons to use each

Use each when you need keep variables in upstream tables or need to read from upstream-cross tables. Otherwise prefer for.

User Contributed Notes
0 notes + add a note