Skip to main content

Working with Collections

A collection is an object class that contains multiple elements of the same type. These types can be literal values, such as strings or integers, or objects. Each element in a collection consists of a key (the position of that element within the collection) and a value (the data stored at that position). Using collection methods and properties, you can perform actions such as:

  • Access elements based on their keys and modify their values.

  • Insert or delete elements at specified key positions.

  • Count the number of a elements in a collection.

InterSystems IRIS® provides two kinds of collections: list and arrays.

  • In list collections, elements are ordered sequentially. The keys in a list are integers from 1 to N, where N is the last element in the list. This example sets the second element of a list (that is, the element with a key of 2) to the string value red.

     do myList.SetAt("red",2)
  • In array collections, elements are accessed based on arbitrary key names that you specify when you create the element. You can specify string or numeric key names. This example sets the color key of an array to the string value red.

     do myArray.SetAt("red","color")

    Elements in an array are ordered by key, with numeric keys first, sorted from smallest to largest, and string keys next, sorted alphabetically with uppercase letters coming before lowercase letters. For example: -2, -1, 0, 1, 2, A, AA, AB, a, aa, ab.

Defining Collections

You can define collections as properties of any object class. You can also define collections outside of properties by using standalone collections.

Defining Collection Properties

To define list or array properties, use these property definition syntaxes:

Property MyProp as list of Type;
Property MyProp as array of Type;

Here, MyProp is the property name, and Type is either a data type class or an object class.

This example shows how to define a property for a list of %StringOpens in a new tab values.

Property Colors as list of %String;

This code shows how to define a property for an array of Doctor objects.

Property Doctors as array of Doctor;

InterSystems IRIS stores collection properties as instances of classes in the %Collection package. These classes contain the methods and properties that you can use to work with collections.

This table summarizes the ways to define collection properties and the %Collection classes used to store them.

Collection Type %Collection Class
List of strings, integers, or other data type
%Collection.ListOfDTOpens in a new tab
List of objects
%Collection.ListOfObjOpens in a new tab
Array of strings, integers, or other data type
%Collection.ArrayOfDTOpens in a new tab
Array of objects
%Collection.ArrayOfObjOpens in a new tab
Note:

Do not use the %Collection classes directly as the type of a property. For example, do not create a property definition like this:

Property MyProp as %Collection.ArrayOfDT;

Defining Standalone Collections

As an alternative to defining collection properties, you can define standalone collections, such as for use as a method argument or return value. To define a standalone collection, InterSystems IRIS® provides classes in the %Library package that provide similar functionality to the %Collection classes used to store collection properties.

To create a standalone collection, call the %New() method of the suitable class to obtain an instance of that class. Then use methods of that instance to perform collection operations. For example, this code creates a list of strings using the %Library.ListOfDataTypesOpens in a new tab class, inserts three elements into the list, and then displays the number of elements in the list:

 set mylist=##class(%ListOfDataTypes).%New()
 do mylist.Insert("red")
 do mylist.Insert("green")
 do mylist.Insert("blue")
 write mylist.Count()

This table summarizes the ways to define standalone collections and the %Library classes used to create them.

Collection Type %Library Class Sample Creation Syntax
List of strings, integers, or other data type
%ListOfDataTypesOpens in a new tab
set myList = ##class(%ListOfDataTypes).%New()
List of objects
%ListOfObjectsOpens in a new tab
set myList = ##class(%ListOfObjects).%New()
Array of strings, integers, or other data type
%ArrayOfDataTypesOpens in a new tab
set myArray = ##class(%ArrayOfDataTypes).%New()
Array of objects
%ArrayOfObjectsOpens in a new tab
set myArray = ##class(%ArrayOfObjects).%New()

Working with List Collections

The examples in these sections show how to work with list properties, but you can use similar syntaxes to work with lists that are in a standalone collection.

Insert List Elements

To insert an element at the end of a list, use the Insert() method. For example, suppose that obj is a reference to an object, and Colors is a list property of the associated object. This property is defined as follows:

Property Colors as list of %String;

This code inserts three elements into the list. If the list was previously empty, then these elements are located at positions 1, 2, and 3, respectively.

 do obj.Colors.Insert("Red") // key = 1
 do obj.Colors.Insert("Green") // key = 2
 do obj.Colors.Insert("Blue") // key = 3

To insert an element at a specific position within a list, use the InsertAt() method. For example, this code inserts the string Yellow into the second position of the Colors property of obj.

 do obj.Colors.InsertAt("Yellow",2)

The list elements now have this order: "Red", "Yellow", "Green", "Blue". The new element is at position 2 and the elements previously at position 2 and 3 ("Green" and "Blue") move to positions 3 and 4 to make room for the new element.

The insertion of list elements works the same for objects. For example, suppose that pat is an object reference, and Diagnoses is a list property of the associated object. This property is defined as follows, where PatientDiagnosis is the name of a class:

Property Diagnoses as list of PatientDiagnosis;

This code creates a new class instance of PatientDiagnosis, stored in object reference patdiag, and inserts this object at the end of the Diagnoses list.

 Set patdiag = ##class(PatientDiagnosis).%New()
 Set patdiag.DiagnosisCode=code
 Set patdiag.DiagnosedBy=diagdoc
 Set status=pat.Diagnoses.Insert(patdiag)

Access List Elements

To access list elements, you can use these collection methods:

  • GetAt(key) – Return the element value at the position specified by key.

  • GetPrevious(key) – Return the element value at the position immediately before key.

  • GetNext(key) – Return the element value at the position immediately after key.

  • Find(value, key) – Starting after key, return the key of the next list element that equals value.

For example, this code iterates over a list and displays the elements of that list in order. The Count property of collections determines the number of elements to iterate over.

 set p = ##class(Sample.Person).%OpenId(1)
 for i = 1:1:p.FavoriteColors.Count() {write !, p.FavoriteColors.GetAt(i)}

Modify List Elements

To modify a value at a specified key, you can use the SetAt() method, as shown by this syntax:

 do oref.PropertyName.SetAt(value,key)

Here, oref is an object reference and PropertyName is the name of a list property of that object. For example, suppose that person.FavoriteColors is a list property of favorite colors containing elements red, blue, and green. This code changes the second color in the list to yellow:

 do person.FavoriteColors.SetAt("yellow",2)

Remove List Elements

To remove a list element, use the RemoveAt() method. For example, suppose that person.FavoriteColors is a list property of favorite colors containing elements red, blue, and green. This code removes the element at position 2 (blue)

 do person.FavoriteColors.RemoveAt(2)

The list elements now have this order: red, green. The element previously at position 3 (green) moves to position 2 to fill the gap caused by the removed element.

Working with Array Collections

To add an element to an array, use the SetAt() method. For example, myArray.SetAt(value,key) sets the array element at position key to the specified value. This code inserts a new color into an array of RGB values:

 do palette.Colors.SetAt("255,0,0","red")

Here, palette is a reference to the object that contains the array, Colors is the name of the array property, and "red" is the key to access the value "255,0,0".

Important:

Do not include a sequential pair of vertical bars (||) within the value that you use as the array key. This restriction is imposed by the InterSystems SQL mechanism.

The SetAt() methods also modifies the values of existing elements. For example, this code changes the value stored at the "red" key to the hexadecimal format:

 do palette.Colors.SetAt("#FF0000","red")

To iterate over the contents in an array, pass the key by reference to the GetNext() method, which causes the loop to iterate over both keys and values. For example, this code writes the keys and values of an string array in order. The elements are ordered alphabetically by key.

 set arr=##class(%ArrayOfDataTypes).%New()
 do arr.SetAt("red","color")
 do arr.SetAt("large","size")
 do arr.SetAt("expensive","price")

 set key=""
 for  {set value=arr.GetNext(.key)  quit:key=""  write !,key," = ",value}
 color = red
 price = expensive
 size = large

Copying Collection Data

To copy the items in one collection into another collection, set the recipient collection equal to the source collection. This copies the contents of the source into the recipient (not the OREF of the collection itself). Some examples of such a command are:

 Set person2.Colors = person1.Colors
 Set dealer7.Inventory = owner3.cars

where person2, person1, dealer7, and owner3 are all instances of classes and Colors, Inventory, and cars are all collection properties. The first line of code looks as it might for copying data between two instances of a single class and the second line of code as it might for copying data from an instance of one class to an instance of a different class.

If the recipient collection is a list and the source collection is an array, InterSystems IRIS copies only the data of the array (not its key values). If the recipient collection is an array and the source collection is a list, the InterSystems IRIS generates key values for the recipient array; these key values are integers based on the position of the item in the source list.

Note:

There is no way to copy the OREF from one collection to another. It is only possible to copy the data.

Controlling the SQL Projection of Collection Properties

As described earlier in this book, a persistent class is projected as an SQL table. This section describes how list and array properties are projected by default and how you can modify those SQL projections.

Default Projection of List Properties

By default, a list property is projected to SQL as a single column containing multiple values. To find an item in this column, use the FOR SOME %ELEMENT predicate. For example, this query returns the rows in which the FavoriteColors column, which is a projection of the FavoriteColors list property, contains the element 'Red'.

SELECT * FROM Sample.Person WHERE FOR SOME %ELEMENT (FavoriteColors) (%VALUE = 'Red')

Alternatively, you can use the %INLIST predicate to either find an element in a list or find a list that does not contain a certain element. For example, this query returns the rows in which the FavoriteColors column does not contain the list element 'Red'.

SELECT * FROM Sample.Person WHERE NOT ('Red' %INLIST (FavoriteColors))

If the list for a particular instance contains no elements, it is projected as an empty string (and not an SQL NULL value).

Default Projection of Array Properties

By default, an array property is projected as a child table, which is in the same package as the parent table. The name of this child table is as follows:

tablename_fieldname

where:

  • tablename is the SqlTableName of the parent class (if specified) or the short name of the parent class (if SqlTableName is not specified).

  • fieldname is the SqlFieldName of the array property (if specified) or the name of the array property (if SqlFieldName is not specified).

For example, consider a Person class with an array property called Siblings. The projection of the Siblings property is a child table called “Person_Siblings”.

The child table contains these columns:

  1. A column containing the IDs of the corresponding instance of the parent class. This column acts as a foreign key to the parent class containing the array and is named after that class. In the projected Person_Child table, this column is named Person.

  2. A column named element_key that contains the identifier for each array member.

  3. A column containing the value of each array member. This column is named after the array property. In the projected Person_Child table, this column is named Siblings.

This table shows sample entries and the generated column names of the Siblings child table.

Sample Projection of an Array Property
Person element_key Siblings
10 C Claudia
10 T Tom
12 B Bobby
12 C Cindy
12 G Greg
12 M Marsha
12 P Peter

If an instance of the parent class holds an empty collection (one that contains no elements), the ID for that instance does not appear in the child table, such as the instance above where ID equals 11.

Notice that there is no Siblings column in the parent table.

For the column(s) containing the array members, the number and contents of the column(s) depend on the kind of array:

  • The projection of an array of data type properties is a single column of data.

  • The projection of an array of reference properties is a single column of object references.

  • The projection of an array of embedded objects is as multiple columns in the child table. The structure of these columns is described in the section “Embedded Object Properties.”

Together, the ID of each instance and the identifier of each array member comprise a unique index for the child table. Also, if a parent instance has no array associated with it, it has no associated entries in the child table.

Note:

A serial object property is projected to SQL in the same way, by default.

Important:

When a collection property is projected as an array, there are specific requirements for any index you might add to the property. See “Indexing Collections” in SQL Optimization Guide. For an introduction to indexes in InterSystems IRIS persistent classes, see the chapter “Other Options for Persistent Classes.”

Important:

There is no support for SQL triggers on child tables projected by array collections. However, if you update the array property and then save the parent object using ObjectScript, any applicable triggers will fire.

Alternative Projections of Collections

This section discusses the STORAGEDEFAULT, SQLTABLENAME, and SQLPROJECTION property parameters, which affect how collection properties are stored and projected to SQL.

STORAGEDEFAULT Parameter

You can store a list property as a child table, and you can store an array property as a $LIST. In both cases, you specify the STORAGEDEFAULT parameter of the property:

  • For a list property, STORAGEDEFAULT is "list" by default. If you specify STORAGEDEFAULT as "array", then the property is stored and projected as a child table. For example:

    Property MyList as list of %String (STORAGEDEFAULT="array");

    For details on the resulting projection, see “Default Projection of Array Properties.”

  • For an array property, STORAGEDEFAULT is "array" by default. If you specify STORAGEDEFAULT as "list", then the property is stored and projected as a list. For example:

    Property MyArray as array of %String (STORAGEDEFAULT="list");

    For details on the resulting projection, see “Default Projection of List Properties.”

Important:

The STORAGEDEFAULT property parameter affects how the compiler generates storage for the class. If the class definition already includes a storage definition for the given property, the compiler ignores this property parameter.

SQLTABLENAME Parameter

If a collection property is projected as a child table, you can control the name of that table. To do so, specify the SQLTABLENAME parameter of the property. For example:

Property MyArray as array of %String(SQLTABLENAME = "MyArrayTable");

Property MyList as list of %Integer(SQLTABLENAME = "MyListTable", STORAGEDEFAULT = "array");

The SQLTABLENAME parameter has no effect unless the property is projected as a child table.

SQLPROJECTION Parameter

By default, if a collection property is stored as a child table, it is also projected as a child table, but it is not available in the parent table. To make such a property also available in the parent table, specify the SQLPROJECTION parameter of the property as "table/column"

For example, consider the following class definition:

Class Sample.Sample Extends %Persistent
{

Property Property1 As %String;

Property Property2 as array of %String(SQLPROJECTION = "table/column");

}

The system generates two tables for this class: Sample.Sample and Sample.Sample_Property2

The table Sample.Sample_Property2 stores the data for the array property Property2, as in the default scenario. Unlike the default scenario, however, a query can refer to the Property2 field in the Sample.Sample table. For example:

MYNAMESPACE>>SELECT Property2 FROM Sample.Sample where ID=7
13.     SELECT Property2 FROM Sample.Sample where ID=7
 
Property2
"1     value 12       value 23       value 3"

The SELECT * query, however, does not return the Property2 field:

MYNAMESPACE>>SELECT * FROM Sample.Sample where ID=7
14.     SELECT * FROM Sample.Sample where ID=7
 
ID      Property1
7       abc

See Also

FeedbackOpens in a new tab