Use Array Functions for Multi-Select Widgets and Looping

Here’s how to use an array function to create a configuration wizard widget that lets the user select multiple values.

Create a Multi-Select Widget 

This ArrayType variable creates a wizard widget that lets the user select multiple values.

1"Account_Fields": {
2        "label": "Pick the fields for your dataset",
3        "description": "Multiselect sobjectfield test",
4        "defaultValue": [
5          {
6            "sobjectName": "Account",
7            "fieldName": "Name"
8          }
9        ],
10        "required": false,
11        "variableType": {
12            "type": "ArrayType",
13            "itemsType": {
14                "type": "SobjectFieldType"
15            }
16        },
17        "excludeSelected": true
18    }

Results in the following wizard widget:

This diagram depicts the resulting wizard question.

The ArrayType variable contains an itemsType attribute, which accepts the following:

  • SObjectType
  • SObjectFieldType
  • DatasetType
  • DatasetDateType
  • DatasetDimensionType
  • DatasetMeasureType
  • StringType
  • NumberType

How the Widget Stores Selections 

The widget places values in an array. The following show how the widget displays values for the user to select:

These two images depict how selections in the widget are placed into an array.

These two images depict how selections in the widget are placed into an array.

Reference Array Items by Index 

Items in the array can be referenced by index. Arrays of StringType and NumberType are easy to work with, but arrays of SObjectType and SObjectFieldType contain multiple attributes for each item in the array.

Array of SobjectFieldType:

ExpressionReturns
${Variables.Account_Fields[0]}(sobjectName=Account, fieldName=Name, sobjectLabel=Account, fieldLabel=Account Name)
${Variables.Account_Fields[0].fieldName}Name
${Variables.Account_Fields[0].fieldLabel}Account Name

fieldName is required for dataflow.

Note

Access Array Values in Template Files 

The selection values from the widget are stored in the variable. There are two ways to access them in the template files (rules, dashboards, workflow).

Without looping

Use array values from SobjectFieldType by index.

1"fields":[
2        {"name":"${Variables.Account_Fields[0].fieldName}"},
3        {"name":"${Variables.Account_Fields[1].fieldName}"},
4        {"name":"${Variables.Account_Fields[2].fieldName}"},
5        {"name":"${Variables.Account_Fields[3].fieldName}"},
6        {"name":"${Variables.Account_Fields[4].fieldName}"}
7    ]

This example, without looping, would be tricky to use, because there would always need to be at least five items in the array. Any fewer would fail, and anything over five would not be used.

With looping

Use array values from SobjectFieldType with the array:forEach function. In this example, a “set” action loops through the SObjectFieldTypes in the array and adds them to the “fields” attribute in the Extract_Account step of the workflow. Each entry in “fields” needs “name” and “var.fieldName”.

rules.json

1"name": "AddAccountFilesToDataflow",
2  "appliesTo": [
3    {
4      "type": "workflow",
5      "name": "*"
6    }
7  ],
8  "actions": [
9    {
10      "action": "set",
11      "description": "use selected values for sfdcDigest in dataflow",
12      "path": "$.workflowDefinition.Extract_Account.parameters.fields",
13      "value": "${array:forEach(Variables.Account_Fields, '{\"name\": \"${var.fieldName}\"}')}"
14    }
15  ]

dataflow.json

1"Extract_Account":{
2   "action":"sfdcDigest",
3   "parameters":{
4       "fields":[],
5       "object":"Account"
6   }
7}

Result after processing

If Account Name, Account Source, and Industry are selected:

1"Extract_Account":{
2    "action":"sfdcDigest",
3    "parameters":{
4        "fields":[
5            {"name":"Name"},
6            {"name":"AccountSource"},
7            {"name":"Industry"}
8        ]
9    }
10]

Combine Arrays 

These functions extend the power of array:forEach.

array:union

Uses only unique values, so no values are duplicated.

1"value": "${array:union(array:forEach(Variables.Account_Fields,
2 '{\"name\": \"${var.fieldName}\"}'),array:forEach(Variables.Account_Fields2,
3 '{\"name\": \"${var.fieldName}\"}'))}"

array:concat

Two new arrays:

1"value": "${array:concat(array:forEach(Variables.Account_Fields,
2'{\"name\": \"${var.fieldName}\"}'),array:forEach(Variables.Account_Fields2,
3 '{\"name\": \"${var.fieldName}\"}'))}"

One new array with an existing array:

1"value":"${array:concat(Rules.CurrentNode,
2 array:forEach(Variables.Account_Fields,
3 '{\"name\":\"${var.fieldName}\"}'))}"

Which actions support array functions

You can use array functions in the “set” and “put” actions, but not in the “add” or “delete” actions.

In “add”, the value is being added to an existing array, so the result is an array inside an array, which is not well-formed JSON. To add more array values to an existing array, use “set” with the array:concat function. For “delete”, value is not used.

Note

Use StringType and NumberType Arrays 

You can use array values from StringType the same way as from NumberType.

variables.json

1"Account_Fields_String": {
2        "label": "Pick string fields for your dataset",
3        "description": "Multiselect string test",
4        "defaultValue": ["Name"],
5        "required": true,
6        "variableType": {
7            "type": "ArrayType",
8            "itemsType": {
9                "type": "StringType",
10                "enums": ["Name", "AccountSource", "Industry", "Type"]
11            }
12        }
13     }

rules.json

1"value": "${array:forEach(Variables.Account_Fields_String,
2 '{\"name\":
3        \"${var}\"}')}"

Replace Multiple Yes/No Widgets 

You can also use the multiselect widget to replace multiple StringType widgets with “Yes” and “No” selections. This example uses three StringType widgets on one wizard page:

This image depicts an example of three stringtypes in one wizard page.

You can replace this with a single ArrayType widget:

This image depicts an example of replacing three stringtypes with a single arrayType widget.

The JSON for this variable is:

1"SelectedFeatures": {
2    "label": "Please select the features you would like to enable?",
3    "description": "The selected features for this app",
4    "defaultValue": [
5    	"Apex Execution"
6    ],
7    "variableType": {
8      "type": "ArrayType",
9      "itemsType" : {
10        "type" : "StringType",
11        "enums" : [
12        	"Apex Execution",
13        	"API",
14        	"Content Transfer",
15        	"Dashboard",
16        	"Login",
17        	"Report Export",
18        	"Rest API",
19        	"Setup Audit Trail",
20        	"UI Tracking",
21        	"URI",
22        	"Visualforce Request",
23        	"Wave Change",
24        	"Wave Interaction",
25        	"Wave Performance"
26        ]
27  	  }
28    },
29    "required": true
30  }

You can use the selected values to set constants in rules.json, which you can then reference in conditionals for actions or in template-info.json.

1"constants" : [
2	{"name":"hasApexExecution", "value": "${array:contains(Variables.SelectedFeatures, 'Apex Execution')}"},
3	{"name":"hasAPI", "value": "${array:contains(Variables.SelectedFeatures, 'API')}"},
4	{"name":"hasContentTransfer", "value": "${array:contains(Variables.SelectedFeatures, 'Content Transfer')}"},
5	{"name":"hasDashboard", "value": "${array:contains(Variables.SelectedFeatures, 'Dashboard')}"},
6	{"name":"hasLogin", "value": "${array:contains(Variables.SelectedFeatures, 'Login')}"},
7	{"name":"hasReport", "value": "${array:contains(Variables.SelectedFeatures, 'Report Export')}"},
8	{"name":"hasRestApi", "value": "${array:contains(Variables.SelectedFeatures, 'Rest API')}"}
9]