Complex arrays are arrays of objects that can optionally contain nested arrays. Complex arrays are mapped using the array.forEach() function. The array.forEach() function can be used with or without arguments.
Note: The array.forEach() function cannot be used within any other function or expression.
For examples, refer to
https://github.com/TIBCOSoftware/tci-flogo/tree/master/samples/app-dev/Mapping-Arrays/array.forEach.sample.
When you use array.forEach() without any arguments, you define an implicit scope consisting of everything available in the Upstream Output. It is equivalent to creating an
implicit array with a single object element consisting of everything in the Upstream Output. Hence, the resulting length of the array is always one element.
To create a confined scope within the Upstream Output, use array.forEach() with
arguments. You can do so by entering the mapping manually or by selecting the forEach()
function under the array category under Functions. The forEach() function can accept three arguments. When mapping identical arrays, the array.forEach() function gets inserted with the first two arguments by default.
The first argument defines the scope within the Upstream Output. Simply put, the input object or array can only be mapped to elements in the Upstream Output that fall within the boundary indicated by its scope.
Note: You can add a function in the first argument of the array.forEach()
function. For example:
array.forEach(array.slice($activity[Mapper], 1, 4), "data")
The second argument is a scoping variable given to the scope that you have defined in the first argument. The scoping variable name by default, is the same as the input element name for which you are defining the scope. By doing so, the mapper associates the input object to its scope by the scoping variable. Once there is a scoping variable for the scope, the mapper uses that scoping variable to refer to the scope in future mappings. You can edit the scoping variable to any string that might be more meaningful to you. The scoping variable is particularly useful when mapping the child elements in nested arrays.
The third argument is optional. When iterating through an upstream output array, you can enter a filter to specify a particular condition for mapping as the third argument. When using the filter as the third argument, you must mandatorily enter the scoping variable as the second argument. Only array elements that match the filter get mapped. For instance, if you are iterating through an array, array1, in the upstream output with a filter that says
$loop.name=="Jane" as the third argument, if array1 has ten elements and only four out of them match the condition of the filter, only those four elements are mapped to the input array and the remaining six are skipped. This results in the size of the input array to be only four elements, even though array1 has ten elements. See the section, Filtering Array Elements to Map Based on a Condition for more details.
Note: If you have used the array.forEach() in a legacy app, to update your app with the current changes in the array.forEach() function, delete the old
mapping and remap the elements. A scoping variable is now included in the mapping. For example, if the old mapping is: array.forEach($flow.body.Book),
after the remap, the mapping should be: array.forEach($flow.body.Book,
"Book") where "Book" is the scoping variable.
Note: If you use a function as a source array for array.foreach(), Studio cannot determine the array element schema and throws a design-time validation error. It is recommended that you use the mapper to define the function output schema and then use it in the array.foreach() source array.
Understanding array.ForEach() Function with an Example The example in this section illustrates an array, cakes.
Consider the example below which is available for you to experiment with at
},
{
"type":"Glazed" through nested arrays while iterating through the parent array.
The array.forEach() function can take three arguments:
1. The first argument is the source array to iterate over.
2. The second argument is the scope, which typically consists of the array you are iterating over and so has the same name as that array.
3. The third optional argument is the condition to use to pull information when looping through the array.
l "cakes" is the scope name. This is the scope for your mapping. Each scope has a name to it. By default, the scope name is the same as the name of the source array, in this case "cakes".
l $loop.type=="donut" is the condition to filter the array elements.
To filter the cakes array based on type "donut", you would use the following:
Mapping Identical Arrays of Objects
When mapping an array of objects in the input to an identical array of objects (matching property names and data types) in the Upstream Output, keep the following in mind:
l Map the array at the root level. The array.forEach() function automatically gets inserted with the array scope and a scoping variable for the scope as its arguments.
You need not map the array object properties individually if you want all properties to be mapped and if the object property names are identical. The properties get automatically mapped.
l If you do not want all the properties within the object to be mapped or if the names of object properties do not match, you must map the object properties individually too after mapping the root. If you do not do the child mapping individually, the mismatched properties in the objects remain unmapped if the properties are not marked as required (marked with a red asterisk). If such a property is marked as required, then you see a warning.
l The size of the input array is determined by the size of the array in the Upstream Output to which you are mapping.
To map identical arrays of objects:
Procedure
1. Click on the input array root (objArray1 in the example image below).
2. Click the array you want to map to in Upstream Output (objArray in the image below). The array.forEach() function appears in the text box. If the names of all the child elements match, the child elements get mapped automatically. You need not match each child element individually. In this example, none of the child names match, so you would need to do the individual mapping otherwise none of the elements get mapped.
Shown below is the syntax of array.forEach() in the image above :
The "objArray1" in red font is the scoping variable which constitutes the scope of the current input array. Basically, this means that you can map any element in objArray1 with an element of same data type in flow.objArray in the Upstream Output. So, you are defining the scope of objArray1 to be all the elements within objArray.
Mapping Array Child Elements to Non-Array Elements or to an Element in a Non-Matching Array
There may be situations when you want to map an element within an array of objects to
Upstream Output. In such a situation, you must create an array with a single element. You do this by using the array.forEach() function without any arguments. When you use
array.forEach() function without arguments, it creates an array with a single object element. The single object element treats everything in the Upstream Output as the children of the newly created array object element. This allows you to map to any of the Upstream Output elements as they are now treated as if they were within an array.
Important: When using array.forEach() without arguments, be sure to map the child elements individually too in addition to setting the array root to
array.forEach() without arguments. If you do not map the child elements individually, no child elements get mapped. Only elements that you have specifically mapped acquire the mapped values.
Note: Keep in mind that in this scenario, the resulting length of the array is always one element.
Mapping an array child element to a non-array element is a two-step process:
Procedure
1. Click the input array root (objArray in the example below) and either manually enter
array.forEach() without arguments or select the function from the array category under Functions and delete the placeholder arguments.
This creates an array of objects with a single element in it. The element contains everything under Upstream Output, hence allowing you to map to any element in the Upstream Output. The element you are mapping to can be a non-array element or reside within a nested array.
2. Map each element in the input array individually to any element of the same data type under Upstream Output.
In case you are mapping to an element inside an array, you must provide the index of the array. If you are mapping to an element in a nested array, you must provide the index for both the parent and the nested array as shown above.
Mapping Nested Arrays
Before you map a nested array, you must map its parent root. The scoping variable is particularly useful when mapping the child elements in nested arrays.
The example below is that of a nested array, where Address is a nested array whose parent is Customer:
To map Address, do the following:
Procedure
1. Map its parent, Customer. When you map Customer, you automatically set the scope of Customer.
In the image above, Customer is mapped to Customer1. The first argument,
$flow.Customer1 is the source array (from which Customer gets the data) that you are mapping to. This defines the scope (boundary) in the Upstream Output within which you can map Customer. So, this is the scope of Customer. The second argument, "Customer", is the scoping variable given to this scope - the loop here refers to the iteration of Customer. By default, the scoping variable has the same name as the loop for which the scope is being defined (in this case Customer). You can edit the scoping variable to any string that might be more meaningful to you.
This is equivalent to saying that mapping of a child element of Customer can happen only to children of Customer1 in Upstream Output.
2. Map Address. Now the scope of Address gets defined.
Notice that the mapping for Address:
l contains the parent scope as well. The parent scope is referred to by its scoping variable, "Customer". Remember that the scope of Customer was set when you mapped Customer to Customer1 in the first step above, so we can now simply refer to the parent scope by its scoping variable, "Customer". l $loop["Customer"] refers to the iteration of the Customer1 array. $loop
represents the memory address of the Customer1 (the scope for Customer) in Upstream Output.
l $loop["Customer"].Address1 is the scope of Address. This scope is denoted
by the scoping variable "Address", which is the second variable in this mapping. Since Address is a nested array of Customer, when you map to Address or its child elements, its mapping includes the scope of Customer as well.
Mapping Child Elements within a Nested Array Scope
A child element in the input array can be directly mapped to a child element of the same data type within the array scope. Since mapping is done within the nested array scope, you do not need to explicitly state the scoping variable for the nested array scope. The mapping appears as $loop.<element>.
To map a nested array child element:
Procedure
1. Map the parent of the nested array.
2. Map the nested array itself.
3. Map the nested array child elements if the names are not identical or if you do not want to map all elements in the nested array.
In the following example, since street is within the scope of address1, street1 is directly mapped to street. $loop in the following example implicitly points to address which is the scope for address1 in the input schema.
Mapping a Nested Array Child Element outside the Nested Array Scope
To map a nested array child element outside the nested array scope but within its parent array, you must use the scoping variable of the parent array.
To do so:
Procedure
1. Map the parent array root.
2. Map the nested array root.
3. Map the nested array child element.
In the example below, $loop implicitly points to address. In addition, the mapping also explicitly specifies the scope of the parent, "objArray1". This is because zip1 is mapped to code which is outside the scope of address1, but within the scope of its parent array, objArray1.
Mapping an Element from a Parent Array to a Child Element in a Nested Array within the Parent
When mapping a primitive data type child element of the parent array to a child element of its nested array, the scope in the mapping is implicitly set to the scope of the parent array. In addition, you must provide the index of the nested array element whose variable you want to map to.
To do so:
Procedure
1. Map the parent array root.
2. Map the nested array root.
3. Map the parent array element.
In the example below, $loop is implicitly set to the scope of Customer which is MyCustomer. Notice that you must provide the index of the object in the MyAddress array whose MyCountry element you want to map to.
Filtering Array Elements to Map Based on a Condition
When mapping arrays of objects, you can filter the objects that are mapped by specifying a filter in the mapper.
You specify this filter as the third argument in the array.forEach() function, the first argument being the scope of the element being mapped and the second argument being the scoping variable.
Before you begin
To specify the filter as the third argument, you must mandatorily specify both the first two arguments in array.forEach() - the scope as well as the scoping variable.
Here's an example that contains a filter as the third argument:
The above example indicates the following:
l objArray1 is being mapped to objArray in Upstream Output
l when iterating through objArray in the Upstream Output, only the array elements in objArray whose child element, user, is "Jane" get mapped. If user is not equal to
"Jane" the iteration for that object is skipped and objArray1 does not acquire that object.
l $loop here specifies the scope of the current loop that is being iterated, in this case objArray, whose scope is objArray1 in Upstream Output.