Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS Arcade

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 are declared with the var statement and may be assigned any valid type. Variables can be re-assigned new values of different types.

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

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

Variables must be declared before use. Variable names must be a valid Arcade identifier beginning with a letter, contain only letters, numbers, or underscore, and not match any reserved keyword. Variable names are not case-sensitive.

See Global and local scope for details about the scope of variables.

Global variables

Arcade offers special global variables that may be used in 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);

For more complex expressions, we suggest you explicitly list all field attributes that will be used in the execution of the script at the top of the expression. This will ensure the profile, app, and map all request the appropriate fields so the expression executes properly.

$feature.COLLEGE;
$feature.POPULATION;

Round(($feature.COLLEGE / $feature.POPULATION) * 100, 2);

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

Template literals

Since 1.11

Template literals are string literals that allow for embedded expressions. They are enclosed by the backtick character (` `). Template literals can contain placeholders for your expression - indicated by a dollar sign and curly braces (${your_expression_here}). All parts of this template literal will be evaluated as a text value. This allows for easier combination and manipulation of text in Arcade.

`Hello World`
// returns "Hello World"

`Hello
 World`
// returns "Hello
// World"

`There are ${$feature.STUDENTS} students at ${$feature.UNIVERSITY}.`
// returns "There are 15,000 students at the University of California."

var scores = [70, 85, 92, 98, 100, 78, 89];
`The average score was ${Round(Average(scores), 2)}%.`
// returns "The average score was 87.43%."

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.

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

// 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.

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 statement 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
& Bitwise AND Returns a 1 in each bit position for which the corresponding bits of both operands are 1s. If either bit of one of the operands is 0, the corresponding bit of the result is also 0. a & b
| Bitwise OR Returns a 1 in each bit position for which the corresponding bits of either or both operands are 1s. a | b
^ Bitwise XOR Returns a 1 in each bit position for which the corresponding bits of either but not both operands are 1s. a ^ b
~ Bitwise NOT Inverts the bits of its operand. Bits that are 0 become 1 and bits that are 1 become 0. Bitwise NOTing any number x yields -(x+1) ~ a
<< Left shift Shifts a in binary representation b (< 32) bits to the left, shifting in 0s from the right. a << b
>> Sign propagating right shift Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off, and shifting in copies of the leftmost bit from the left. a >> b
>>> Zero fill right shift Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off, and shifting in 0s from the left. The sign bit becomes 0, so the result is always non-negative. a >>> b

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.

Bitwise operators

Since 1.11

Bitwise operators will perform their operation on the binary representation of the decimal number provided to the operator, and will return a standard numerical value.

7 & 14
// converts to 0111 and 1110
// result is 0110, which returns 6

7 | 14
// result is 1111, which returns 15

7 ^ 14
// result is 1001, which returns 9

~ 7
// converts to 00000000000000000000000000000111
// result is 11111111111111111111111111111000 (inverted operand)
// returns -8

7 << 2
// shift 00000000000000000000000000000111 two bits to the left
// result is 00000000000000000000000000011100
// returns 28

7 >> 2
// shift 00000000000000000000000000000111 two bits to the right
// result is 000000000000000000000000000001
// returns 1

-7 >> 2
// shift 11111111111111111111111111111001 two bits to the right
// result is 11111111111111111111111111111110
// returns -2

-7 >>> 2
// shift 11111111111111111111111111111001 two bits to the right
// result is 00111111111111111111111111111110
// returns 1073741822

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 a control block (if, for) will be available outside of the block.

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?