Skip to main content

Understanding Sparse Arrays and Unassigned Values

Understanding Sparse Arrays and Unassigned Values

Dynamic arrays can be sparse arrays, meaning that not all elements of the array contain values. You can, for example, assign a value to element 100 of a dynamic array even if the array does not already contain elements 0 to 99. Space in memory is allocated only for the value at element 100. Elements 0 to 99 are unassigned, meaning that 0 to 99 are valid element identifiers but do not point to any values in memory. The %Size() method would return an array size of 101, but the %GetNext() method would skip over the unassigned elements and return only the value in element 100.

The following example creates a sparse array by assigning new values to elements 8 and 11:

   set array = ["val_0",true,1,"",null,"val_5"] // values 0 through 5
   do array.%Set(8,"val_8")                     // undefined values 6 and 7 will be null
   set array."11" = "val_11"                    // undefined values 9 and 10 will be null
   write array.%ToJSON()

["val_0",true,1,"",null,"val_5",null,null,"val_8",null,null,"val_11"]

No values have been assigned to elements 6, 7, 9, and 10, and they take no space in memory, but they are represented in the JSON string by null values because JSON does not support undefined values.

Using %Remove in sparse arrays

The %Remove() method treats an unassigned element just like any other element. It is possible to have an array that consists of nothing but unassigned values. The following example creates a sparse array and then removes unassigned element 0. It then removes element 7, which is now the only element containing a value:

   set array = []
   do array.%Set(8,"val_8")
   do array.%Remove(0)
   do array.%Remove(7)
   write "Array size = "_array.%Size()_":",!,array.%ToJSON()

Array size = 7:
[null,null,null,null,null,null,null]

See “Using %Set(), %Get(), and %Remove()” for more examples demonstrating %Remove().

Note:
JSON cannot preserve the distinction between null and unassigned values

Dynamic entities contain metadata that allows them to distinguish between null and unassigned values. JSON does not specify a separate undefined datatype, so there is no canonical way to preserve this distinction when a dynamic entity is serialized to a JSON string. If you do not want the extra null values in your serialized data, you must either remove the unassigned elements before serializing (see “Using %IsDefined() to Test for Valid Values”), or use some application-dependent means to record the distinction as metadata.

Sparse Array Iteration with %Size()

The %Size() method returns the number of properties or elements in a dynamic entity. For example:

   set dynObject={"prop1":123,"prop2":[7,8,9],"prop3":{"a":1,"b":2}}
   write "Number of properties: "_dynObject.%Size()

Number of properties: 3

In sparse arrays, this number includes elements with unassigned values, as demonstrated in the following example. The array created in this example has six elements, but only elements 0, 1, and 5 have an assigned value. The null elements displayed in the JSON string are just placeholders for the unassigned values:

   set test=["abc",999]
   set test."5" = "final"
   write test.%Size()_" elements: "_test.%ToJSON()

6 elements: ["abc",999,null,null,null,"final"]

Elements 2, 3, and 4 do not have assigned values, but are still treated as valid array elements. Dynamic arrays are zero-based, so the index number of the final element will always be %Size()-1. The following example iterates through all six elements of array test in reverse order and uses %Get() to return their values:

   for i=(test.%Size()-1):-1:0 {write "element "_i_" = /"_test.%Get(i)_"/",!}

element 5 = /final/
element 4 = //
element 3 = //
element 2 = //
element 1 = /999/
element 0 = /abc/

The %Get() method will return "" (empty string) for numbers greater than %Size()-1, and will throw an exception for negative numbers. See “Working with Datatypes” for information on how to distinguish between unassigned values, empty strings, and null values.

Note:

The iteration technique shown here is useful only for specialized purposes (such detecting unassigned values in an array or iterating over an array in reverse order). In most cases you should use %GetNext(), which skips over unassigned elements and can be used for dynamic objects as well as dynamic arrays. See the previous section (“Iterating over a Dynamic Entity with %GetNext()”) for details.

Using %IsDefined() to Test for Valid Values

The %IsDefined() method tests for the existence of a value at a specified property name or array index number. The method returns 1 (true) if the specified member has a value, and 0 (false) if the member does not exist. It will also return false for elements in a sparse array that do not have assigned values.

Unassigned values will be encountered if you use a for loop to iterate through a sparse array. The following example creates an array where the first three elements are a JSON null, an empty string, and an unassigned value. The for loop is deliberately set to go past the end of the array and test for an element with array index 4:

   set dynarray = [null,""]
   set dynarray."3" = "final"
   write dynarray.%ToJSON()
[null,"",null,"final"]

   for index = 0:1:4 {write !,"Element "_index_": "_(dynarray.%IsDefined(index))}

Element 0: 1
Element 1: 1
Element 2: 0
Element 3: 1
Element 4: 0

%IsDefined() returns 0 in two cases: element 2 does not have an assigned value, and element 4 does not exist.

ObjectScript returns "" (empty string) for JSON null values such as element 0 in this example. If you need to test for "" and null as well as unassigned values, use %GetTypeOf() rather than %IsDefined() (see “Resolving Null, Empty String, and Unassigned Values”).

Note:

As mentioned in the previous section, you should not use a for loop for iteration except in a few unusual situations. In most cases you should use the %GetNext() method, which skips unassigned values (see “Iterating over a Dynamic Entity with %GetNext()”).

The %IsDefined() method can also be used to test for the existence of an object property. The following code creates dynamic array names with three string values, and then uses the first two strings to create object dynobj with properties prop1 and prop2.

   set names = ["prop1","prop2","noprop"]
   set dynobj={}.%Set(names."0",123).%Set(names."1",456)
   write dynobj.%ToJSON()

{"prop1":123,"prop2":456}

The following code uses %IsDefined() to determine which strings have been used as property names in dynobj:

   for name = 0:1:2 {write !,"Property "_names.%Get(name)_": "_(dynobj.%IsDefined(names.%Get(name)))}

Property prop1: 1
Property prop2: 1
Property noprop: 0
FeedbackOpens in a new tab