For each


Description

The for each statement enumerates the elements of a collection, executing an embedded statement for each element of the collection.

Syntax

For Statement Syntax
<For each=”<variable[s]>” in=”<source data key>” from=”<source data key prefix>” using="<variable store key>">
                …facet script …
</For>

Syntax Notes

Examples of each of these variations will be provided in the next section.

Tag

Optional/Required

Description
ForRequiredLoop is implemented as a generic FacetScript construct  and NOT specific to any facet so you can use it anywhere FacetScript can be used.
Each=Required

"each" defines loop variable[s] name[s]

  • One variable form: each=”var_name” this is used to iterate over values

  • Two variables form: each=”key_var:value_var” this is used to iterate over entries (i.e. key:value pairs)
  • Two variables form can be used to define only one (e.g. “key_var:” or “:value_var”)

  • Variable names can be “deep” i.e. contain dots in them (e.g. “obj.type:obj.value”)

in=Required

“in” specifies the source key to iterate over

from=Optional

“from” specifies the source key prefix. Default value is “$Response.Message.Value”. That is, loop will [virtually] concatenate from+in to obtain actual key and some optimizations.

using=Optional

“using” specifies the data store where loop variable[s] will be allocated. Default value is “$LocalData”. That is, since loop body is a scope, a new loop variable[s] will be created for each loop iteration. If you need to pass/keep some data between iteration then you can use a different store (e.g. $ProcessData, $ContextData etc.), combination of stores or wrap loop in another scope and initialize your data there

FAQ

In this section let's try to understand For Each in a FAQ style of documentation. For all questions below: Let's take VendorInfo to contain three Vendors

Iterating over Query Result
<Find Status="Success" Format="all">
    <Result>
        <VendorInfo QName="Atomiton.Sensors.VendorInfo">
            <vendorId>vendorA</vendorId>
            <vendorCity Value="Calgary" Known="Calgary" Version="1" Timestamp="1489093271736" DateTime="2017-03-09 13:01:11.736" QName="Atomiton.Sensors.VendorInfo.vendorCity" FName="vendorCity"/>
            <vendorName Value="Phidget" Known="Phidget" Version="1" Timestamp="1489093271734" DateTime="2017-03-09 13:01:11.734" QName="Atomiton.Sensors.VendorInfo.vendorName" FName="vendorName"/>
        </VendorInfo>
    </Result>
    <Result>
        <VendorInfo QName="Atomiton.Sensors.VendorInfo">
            <vendorId>vendorB</vendorId>
            <vendorCity Value="Calgary" Known="Calgary" Version="1" Timestamp="1489093277295" DateTime="2017-03-09 13:01:17.295" QName="Atomiton.Sensors.VendorInfo.vendorCity" FName="vendorCity"/>
            <vendorName Value="Phidget" Known="Phidget" Version="1" Timestamp="1489093277294" DateTime="2017-03-09 13:01:17.294" QName="Atomiton.Sensors.VendorInfo.vendorName" FName="vendorName"/>
        </VendorInfo>
    </Result>
    <Result>
        <VendorInfo QName="Atomiton.Sensors.VendorInfo">
            <vendorId>vendorC</vendorId>
            <vendorCity Value="Calgary" Known="Calgary" Version="1" Timestamp="1489093282311" DateTime="2017-03-09 13:01:22.311" QName="Atomiton.Sensors.VendorInfo.vendorCity" FName="vendorCity"/>
            <vendorName Value="Phidget" Known="Phidget" Version="1" Timestamp="1489093282311" DateTime="2017-03-09 13:01:22.311" QName="Atomiton.Sensors.VendorInfo.vendorName" FName="vendorName"/>
        </VendorInfo>
    </Result>
</Find>
#QuestionExplaination
1

How do I determine the number of iterations of a for loop?

Iterating over Query Result
<Query>
  <Find format="all">
    <VendorInfo>
      <vendorId ne=""/>
    </VendorInfo>
  </Find>
  <For Each="v" IN="$Response.Message.Value.Find">
    <Log Message="[:$LocalData.v:]"/>
  </For>
</Query>

Number of iterations is always determined by the Number of Containers from the specified source and Each is a single variable format.

Example: Here Source is: $Response.Message.Value.Find

  • For source: $Response.Message.Value.Find. There is only one container with <Find>.
  • For source: $Response.Message.Value.Find.Result. There are three containers with <Result> The size will be 3.
  • For source: $Response.Message.Value.Find.VendorInfo.vendorId. There are three containers with <vendorId> The size will be 3.
2

What happens if I do not specify value in Each i.e Each="k:v"

Iterating over Query Result
<Query>
  <Find format="all">
    <VendorInfo>
      <vendorId ne=""/>
    </VendorInfo>
  </Find>
  <For Each="k:v" IN="$Response.Message.Value.Find.Result.VendorInfo.vendorId">
    <Log Message="In Loop...: [:$LocalData.k:] -> [:$LocalData.v:]"/>
  </For>
</Query>
  • Number of iterations in the loop in this case is 0. Since the starting point is there are no child elements within vendorId that can be broken down into Key / Value pair.
  • If the starting point is made as
    $Response.Message.Value.Find.Result.VendorInfo, number of iterations are 4;

    The key/value is generated for only the last entry.

3

What happens if we provide only one variable in key/value format i.e either k: or :v

Iterating over Query Result
<Query>
  <Find format="all">
    <VendorInfo>
      <vendorId ne=""/>
    </VendorInfo>
  </Find>
  <For Each="k:" IN="$Response.Message.Value.Find.Result.VendorInfo.vendorId">
    <Log Message="In Loop...: [:$LocalData.k:] -> [:$LocalData.v:]"/>
  </For>
</Query>
  • Number of iterations in the loop in this case is 3. LocalData.v: will be unavailable. All the keys are printed i.e

    In Loop...: vendorA -> [:$LocalData.v:]

    In Loop...: vendorB -> [:$LocalData.v:]

    In Loop...: vendorC -> [:$LocalData.v:]

  • If we change it to :v format; the number of iterations in the loop remains as 3; LocalData.k: will be unavailable. All the keys are values are printed i.e.

    In Loop...: [:$LocalData.k:] -> vendorA

    In Loop...: [:$LocalData.k:] -> vendorB

    In Loop...: [:$LocalData.k:] -> vendorC



Tips and Caveats

  • If you use $LocalData your loop variables will NOT be available after the loop (they are defined in the scope of iteration only)
  • We are manipulating structures, so the general idea is to assign a STRUCTURE to a loop variable so you can manipulate the CONTENT of the structure through the variable. That is, <For each=”val” in=”Find.Result.VendorInfo.VendorTitle.Value”> will allow you to do if/select on the val, but will NOT allow you to change the VendorTitle, only the value of loop variable which is a simple value in this case.

    Caveat Example

    Assign STRUCTURE
    <Query>
      <Find format="Version">
        <VendorInfo>
          <vendorTitle eq="Sensor Provider"/>
        </VendorInfo>
      </Find>
      <For each="val" in="Find.Result.VendorInfo.VendorTitle.Value">
        <IF Condition="/'[:$LocalData.val:]' eq 'Sensor Provider'"> <!-- This is simply to show how if can be used -->
           <Log Message="Found Sensor Provider"/>
        </IF>
        <SetLocalData Key="val" Value="Actuator Provider"/> <!-- This will change <Value> only -->
      </For>
      <!-- Do an Update -->
      <Update>
        <From>Result</From>
        <Include>$Response.Message.Value.Find</Include>
      </Update>
    </Query>
     
    <!-- Result is.. -->
    <Find Status="Success" Format="version">
      <Result>
        <VendorInfo>
          <VendorSysId>KQN7B5X7AAAAUAABBN7HUYCE</VendorSysId>
          <vendorTitle Value="Sensor Provider" Version="1"/>
          <vendorName Value="Libelium" Version="2"/>
        </VendorInfo>
      </Result>
      <Result>
        <VendorInfo>
          <VendorSysId>KQN7BUUZAAAAUAABBPXOPXWG</VendorSysId>
          <vendorTitle Value="Sensor Provider" Version="1"/>
          <vendorName Value="Phidget" Version="1"/>
        </VendorInfo>
      </Result>
      <Result>
        <VendorInfo>
          <VendorSysId>KQN7CBXVAAAAUAABBNFHWPBK</VendorSysId>
          <vendorTitle Value="Sensor Provider" Version="1"/>
          <vendorName Value="Phidget3" Version="1"/>
        </VendorInfo>
      </Result>
    </Find>
    <Update Status="Success" Format="version">
      <VendorInfo>
        <VendorSysId>KQN7B5X7AAAAUAABBN7HUYCE</VendorSysId>
        <vendorName Status="Success_NoAction:2:1460829683086;" Value="Libelium" Version="2"/>
        <vendorTitle Status="Success_NoAction:1:1460757657344;" Value="Sensor Provider" Version="1"/> <!-- Note there is no change in value -->
      </VendorInfo>
      <VendorInfo>
        <VendorSysId>KQN7BUUZAAAAUAABBPXOPXWG</VendorSysId>
        <vendorName Status="Success_NoAction:1:1460757648025;" Value="Phidget" Version="1"/>
        <vendorTitle Status="Success_NoAction:1:1460757648027;" Value="Sensor Provider" Version="1"/> <!-- Note there is no change in value -->
      </VendorInfo>
      <VendorInfo>
        <VendorSysId>KQN7CBXVAAAAUAABBNFHWPBK</VendorSysId>
        <vendorName Status="Success_NoAction:1:1460757661429;" Value="Phidget3" Version="1"/>
        <vendorTitle Status="Success_NoAction:1:1460757661429;" Value="Sensor Provider" Version="1"/> <!-- Note there is no change in value -->
      </VendorInfo>
    </Update>

    Correct way is to point val → Find.Result.VendorInfo 

    Assign to STRUCTURE
    <Query>
      <Find format="Version">
        <VendorInfo>
          <vendorTitle eq="Sensor Provider"/>
        </VendorInfo>
      </Find>
      <For each="val" in="Find.Result.VendorInfo">
        <IF Condition="/'[:$LocalData.val.vendorTitle.Value:]' eq 'Sensor Provider'">
          <Log Message="Found Sensor Provider"/>
        </IF>
        <SetLocalData Key="val.vendorTitle.Value" Value="Actuator Provider"/>
      </For>
      <!-- Do an Update -->
      <Update>
        <From>
          Result
        </From>
        <Include>
          $Response.Message.Value.Find
        </Include>
      </Update>
    </Query>
    <!-- Result is -->
    <Find Status="Success" Format="version">
      <Result>
        <VendorInfo>
          <VendorSysId>KQN7B5X7AAAAUAABBN7HUYCE</VendorSysId>
          <vendorTitle Version="3" Value="Actuator Provider"/>
          <vendorName Value="Libelium" Version="2"/>
        </VendorInfo>
      </Result>
      <Result>
        <VendorInfo>
          <VendorSysId>KQN7BUUZAAAAUAABBPXOPXWG</VendorSysId>
          <vendorTitle Version="3" Value="Actuator Provider"/>
          <vendorName Value="Phidget" Version="1"/>
        </VendorInfo>
      </Result>
      <Result>
        <VendorInfo>
          <VendorSysId>KQN7CBXVAAAAUAABBNFHWPBK</VendorSysId>
          <vendorTitle Version="3" Value="Actuator Provider"/>
          <vendorName Value="Phidget3" Version="1"/>
        </VendorInfo>
      </Result>
    </Find>
    <Update Status="Success" Format="version">
      <VendorInfo>
        <VendorSysId>KQN7B5X7AAAAUAABBN7HUYCE</VendorSysId>
        <vendorName Status="Success_NoAction:2:1460829683086;" Value="Libelium" Version="2"/>
        <vendorTitle Status="Success=Updated:4:1460935288963;" Value="Actuator Provider" Version="4"/> <!-- Title is updated -->
      </VendorInfo>
      <VendorInfo>
        <VendorSysId>KQN7BUUZAAAAUAABBPXOPXWG</VendorSysId>
        <vendorName Status="Success_NoAction:1:1460757648025;" Value="Phidget" Version="1"/>
        <vendorTitle Status="Success=Updated:4:1460935288964;" Value="Actuator Provider" Version="4"/> <!-- Title is updated -->
      </VendorInfo>
      <VendorInfo>
        <VendorSysId>KQN7CBXVAAAAUAABBNFHWPBK</VendorSysId>
        <vendorName Status="Success_NoAction:1:1460757661429;" Value="Phidget3" Version="1"/>
        <vendorTitle Status="Success=Updated:4:1460935288964;" Value="Actuator Provider" Version="4"/> <!-- Title is updated -->
      </VendorInfo>
    </Update>

Examples

Iterating over Query Result
<Query>
  <Find format="Version">
    <VendorInfo>
      <vendorSysId ne=""/>
    </VendorInfo>
  </Find>
<For each="val" in="Find.Result.VendorInfo"> <!-- Note that since From is not specified: It will default to $Response.Message.Value -->
    <Log Message="Vendor Name is: [:$LocalData.val.vendorName.Value:]"/>
</For>
</Query>
Using a non-default store
<SetContextData Key="Atomiton.FS.Features.1" Value="Conditional Statements"/>
<SetContextData Key="Atomiton.FS.Features.2" Value="For Statements"/>
<SetContextData Key="Atomiton.FS.Features.3" Value="Case Statements"/>
<!-- Let's iterate over ContextData -->
<For each="val" In="Atomiton.FS.Features" From="$ContextData">
    <Log Message="...[:$LocalData.val:]"/>
</For>
Specify the local store
<SetContextData Key="Atomiton.FS.Features.1" Value="Conditional Statements"/>
<SetContextData Key="Atomiton.FS.Features.2" Value="For Statements"/>
<SetContextData Key="Atomiton.FS.Features.3" Value="Case Statements"/>
<!-- Let's iterate over ContextData and use ProcessData to store iterated data -->
<For each="val" in="Atomiton.FS.Features" from="$ContextData" using="$ProcessData">
  <Log Message="within loop...[:$ProcessData.val:]"/>
</For>
Looping from ContextData
<For each="light" in="Message.Value.lights" from="$ContextData.response">
  <SetContextData key="state">
          <value>
              <intensityLevel>-1</intensityLevel>
              <powerConsumption>-1</powerConsumption>
              <reliability>0</reliability>
            </value>
          </SetContextData>
          <if condition="[:$LocalData.light.location/count(latitude) gt 0:]">
            <then>
             ...
			</then>
		</if>
</For>       
Loop with Each in key:val format
<For each="key:val" in="[:SUBSCRIPTIONS:]" from="$FacetData">
  <if condition="/'[:$LocalData.key:]' = '[:$Macro.Argument.Name:]'">
          <Log Message="[:$LocalData.val:]"/>
  </if>
</For>