Structure and Logic

This page provides an overview on the logical statements and operators understood by Arcade. Also note that the following logical functions are available for your convenience that be used in place of some of the syntax mentioned below:

Case sensitivity

Arcade is case insensitive. This means it does not matter if you mix the casing of variable names and function names.

Comments

Comments in Arcade can be set using the following syntax:

  • Single-line comments must be preceded by //
  • Multi-line comments must be enclosed within /* */
// This is a single-line comment

/*
This is a multi-
line comment
*/

Multi-line statements

Scripts may be multi-line statements. Lines are separated by carriage returns. Statements should be separated by semicolons, but may be omitted if the statements are spread over several lines, but are clearly separate commands.

The return command is not required at the end of a script (though it is for custom functions), but it may used for clarity.

var x = 10;
var y = 100;
return x*y;

Variables

Variables may be declared and used to assign values. Variables can be re-assigned new values of different types.

var x = 10;
return x; // returns 10

x = "hello";
return x; // returns "hello"

Global variables

Global variables may be used in Arcade expressions depending on the execution profile:

  • $datastore
  • $feature
  • $layer
  • $map
  • $value
  • $view.scale

You can access feature attributes as global variables using the $feature.fieldName syntax.

// returns % of population with a college degree
var percentCollege = Round(($feature.COLLEGE / $feature.POPULATION) * 100, 2);

// returns the population density per square kilometer
var popDensity = Round( $feature.POPULATION / AreaGeodetic(Geometry($feature), "square-kilometers") );

Attribute values can be referenced using the . syntax or with square brackets.

// returns % of population with a college degree
Round(($feature["COLLEGE"] / $feature["POPULATION"]) * 100, 2);

You can also reference values from joined tables using this syntax: $feature["joinKey.fieldName"]

// returns % change in votes from 2012 to 2016
Round((($feature["COUNTY_ID.VOTED_DEM_2016"] - $feature["COUNTY_ID.VOTED_DEM_2012"]) / $feature["COUNTY_ID.VOTED_DEM_2012"]) * 100, 2);

Use $view.scale to base a calculation on the map scale.

FeatureSets

Currently, only the Popup, Field Calculate, and Attribute Rule profiles support FeatureSets.

A FeatureSet represents a connection to a set of features in memory or in a server. FeatureSets allow you to access features from feature service layers within the map or feature service. The $layer global variable provides access to all features from the same layer as $feature.

// Returns the highest population among all features in the layer
When(
  $feature.Population > ( Stddev($layer, "Population") + Average($layer, "Population") ), "well above average population",
  $feature.Population < ( Average($layer, "Population") - Stddev($layer, "Population") ), "well below average population",
  "normal population"
);

Or you can create a FeatureSet using one of the functions below, which takes the $map or $datastore as a global variable and returns one if its layers based on its title, layer ID, or portal item ID.

FeatureSets can be chained, which means that some functions will return a FeatureSet and use it as input in the next function in the chain. In the snippet below, the Intersects() function is chained with Filter(). This returns a FeatureSet of polygons classified as sensitive that intersect the given feature. The count of that FeatureSet is then returned.

// returns the number of sensitive land features that intersect the feature displaying the popup
var publicLandFeatures = FeatureSetByName($map, "public lands", ["class"], true);
Count(Intersects(Filter(publicLandFeatures, "class = 'sensitive'"), $feature), "square-kilometers");

Chaining is useful because it allows the script to make a single server request, thus maximizing the performance of the script. However, script performance largely depends on how it is written. For example, you can loop through FeatureSets like the snippet demonstrates below.

var totalArea = 0;
// loops through every feature in the layer
for(var feature in $layer){
  totalArea += AreaGeodetic(feature, "square-kilometers");
}

This will slow down script execution, especially for large layers since all features in the layer will be downloaded to the client. Therefore, it more advisable to loop through a filtered set of features.

var sensitiveFeatures = Filter(publicLandFeatures, "class = 'sensitive'");
var totalArea = 0;

// loops through only the features classified as 'sensitive'
for(var feature in sensitiveFeatures){
  totalArea += AreaGeodetic(feature, "square-kilometers");
}

This will only require the script to download a subset of features to the client.

If statements

IF statements may be used to support conditional logic in the script. See the full list of supported logical operators below.

function isOdd(value){
  // Returns either 1 or 0.
  // 1 = odd number
  // 0 = even number
  // Boolean() converts the raw value to a bool
  var val = Boolean(value % 2);
  if (val) {
    return "Yes. This is odd."
  }
  else {
    return "Nope. Not odd.";
  }
}

isOdd(213);  // returns "Yes. This is odd."
isOdd(2);  // returns "Nope. Not odd."

For loops

Looping is supported with for(initialization, condition, finalExpression){ // logic to execute }.

var result = 0;
for(var z=0; z<100; z++) {
  result += z;
}
return result; // returns 4950

For loops can iterate through arrays.

var myArray = [10,20,30];
for(var k in myArray) {
  n+=myArray[k];   // Note that K iterates through the index not the items
}

For loops may also iterate through keys in a dictionary.

var myDictionary = Dictionary("field1", 1, "field2", 2)
for(var k in myDictionary) {
  n+= myDictionary[k];   // Note that K will be "field1" and then "field2"
}

And they may iterate through items in a FeatureSet.

var sensitiveFeatures = Filter(publicLandFeatures, "class = 'sensitive'");
var largeAreasPresent = false;
for(var feature in sensitiveFeatures){
  largeAreasPresent = IIF(feature.ACRES > 5000, true, largeAreasPresent);
}

Arcade also supports the break and continue statements. Using return inside the loop will exit the loop and function.

for(var i=1; i<100; i++) {
  if (i==3) continue;

  if (i==5) break;
    n+=i;
}

Operators

Arcade provides the following operators.

Bitwise operators are not supported.

Operator Name Description Example
+ Addition Adds two numbers or concatenates two strings. 5+5
- Subtraction Subtracts two numbers. 5-5
* Multiplication Multiplies two numbers. 5*5
/ Division Divides two numbers. 5/5
% Modulus Returns the remainder of a number divided by another number. 10 % 2
+= Add assign Adds a number to a number variable and assigns the result to the variable. x += 10
-= Minus assign Subtracts a number from a number variable and assigns the result to the variable. y -= 10
*= Multiply assign Multiplies a number to a number variable and assigns the result to the variable. x *= 10
/= Divide assign Divides a number variable by a number and assigns the result to the variable. x /= 2
++ Increment by one Increments a number variable by one. x++ or ++x
-- Decrement by one Decrements a number variable by one. --x or x--
|| Logical or Returns true if one of two statements returns true if (x==9 || y==0){}
&& Logical and Returns true if both given statements return true if (x==9 && y==0){}
! Logical not Returns true if the stament does not return true. if(!didItWork) { console("No. It didn't work.") }
== Equal to Evaluates if the two given values are equal to each other. x==y
!= Not equal to Evaluates if the two given values are not equal to each other. x!=y
> Greater than Evaluates if the x-value is greater than the y-value. x>y
>= Greater than or equal Evaluates if the x-value is greater than or equal to the y-value. x>=y
< Less than Evaluates if the x-value is less than the y-value. x<y
<= Less than equal to Evaluates if the x-value is less than or equal to the y-value. x<=y

Increment operators

The increment/decrement by one operators have both a pre and post versions that differ in what they return. The pre increment version adds one to the variable and returns the new value while the post increment adds one and then returns the initial value of the variable.

//Pre increment example
var x=10
++x  // x is now 11 and the value 11 is returned

//Post increment example
var x=10
x++ // x is now 11 but the value of 10 is returned.

Unary operators

Operator Description Example
+ Returns value +10
- Negates value -10

Comparison operators

Comparison operators attempt to coerce strings to a number when comparing a string to a number. The == and != operators don't do any coercion of types. If the types are different, then they are not equal.

10 == "10"  // is false
10 != "10"  // is true
10 > "10"   // is false
10 >= "10"  // is true
10 < "10"   // is false
10 <= "10"  // is true
9  < "10"   // is true
11 < "10"   // is false

For Arrays, Dictionaries, and geometry types, comparisons are a pointer check to see if they are the same object. Internal values of the object are not checked.

var a1 = [1,2,3]
var a2 = [1,2,3]
a1 == a2  // false since they are not the same object.
a1 == a1  // true since they are the same object.

User-defined functions

User defined functions are supported. Functions must be declared before they are used. Functions have local scope, meaning any variables defined in them will only exist during the call to the function.

function adder(x, y) {
  var result = x+y;
  return result*10;
}

adder(10,2);

Global and local scope

Variables have scope. All variables declared at global scope will be available anywhere in the script, including within functions and outside of functions. Variables declared inside of functions can override global scope variables, while inside the function.

var myglobal = 1;
function useGlobal() {
  var n = 10;    // n is only available inside of this function. ie. It is local scope.
  return myglobal * n;    // will use the globally defined variable
}

function overrideGlobal() {
  var myglobal = 1000;
  return myglobal * 10;   // will use the locally defined variable
}

var z = overrideGlobal();   // will be 10000
var k = useGlobal(); // will be 10
return z+k;  // returns 10010

Variables must be declared before use. Block level scoping is not supported. This means variables defined in For loops will be available outside of the loop.

var myglobal = 1;
for(var k=1; k<100; k++) {
  var n=10;  // Defined in Block, but will be available outside of Block.
}
return k + n;  // Will return 110

Feedback on this topic?