One of the Delphi language features not found in other modern languages is the notion of sets.

Delphi's **set type** is a collection of values of the same ordinal type.

A set is defined using the **set of** keyword:

` `**type**
TMagicNumber = 1..34;
TMagicSet = **set of** TMagicNumber;
**var**
emptyMagicSet : TMagicSet;
oneMagicSet : TMagicSet;
anotherMagicSet : TMagicSet;
**begin**
emptyMagicSet := [];
oneMagicSet := [1, 18, 24];
anotherMagicSet := [2, 5, 19];
**if** 1 **in** oneMagicSet **then** ShowMessage('1 is magic, part of oneMagicSet') ;
**end**;

Set types are usually defined with subranges.

In the above example, the TMagicNumber is a custom subrange type allowing variables of the TMagicNumber type to receive values from 1 to 34. Simply put, a subrange type represents a subset of the values in another ordinal type.

Possible values of the set type are all the subsets of the base type, including the empty set.

A limitation on sets is that they can hold up to 255 elements.

In the above example, the TMagicSet set type is a set of TMagicNumber elements - integer numbers from 1 to 34.

The declaration *TMagicSet = set of TMagicNumber* is equal to the following declaration: *TMagicSet = set of 1..34.*

### Set type variables

In the above example, the variables *emptyMagicSet*, *oneMagicSet* and *anotherMagicSet* are sets of TMagicNumber.

To **assign a value** to a set type variable, use the square brackets and list all the elements of the set. As in:

` oneMagicSet := [1, 18, 24]; `

Note 1: every set type variable can hold the empty set, denoted by [].

Note 2: the order of the elements in a set has no meaning, nor is it meaningful for an element (value) to be included twice in a set.

### The IN keyword

To test if an element **is included** in the set (variable) use the **IN** keyword:

` `**if** 1 **in** oneMagicSet **then** ...

### Set Operators

The same way you can sum two numbers, you can have a set that is the sum of two sets. With sets you event have more operators:

- + returns the union of two sets.
- - returns the difference of two sets.
- * returns the intersection of two sets.
- = return true if two sets are equal - have the same elemement.
- <= returns true if the first set is a subset of the second set.
- >= returns true if the first set is a superset of the second set.
- <> returns true if two sets are non-identical.
- IN returns true if an element is included in the set.

Here's an example:

```
emptyMagicSet := oneMagicSet + anotherMagicSet;
emptyMagicSet := emptyMagicSet - [1];
emptyMagicSet := emptyMagicSet + [5,10];
```**if** emptyMagicSet = [2,5,10,18,19,24] **then**
**begin**
emptyMagicSet := emptyMagicSet * oneMagicSet;
ShowMessage(DisplayElements(emptyMagicSet)) ;
**end**;

Will the ShowMessage procedure be executed? If so, what will be displayed?

Here's the implementation of the DisplayElements function:

` `**function** DisplayElements(magicSet : TMagicSet) : **string**;
**var**
element : TMagicNumber;
**begin**
**for** element **in** magicSet **do**
result := result + IntToStr(element) + ' | ';
**end**;

Hint: yes. Displayed: "18 | 24 |".

### Integers, Characters, Booleans

Of course, when creating set types you are not restricted to integer values. Delphi ordinal types include character and boolean values.

To prevent users to type alpha keys, add this line in the OnKeyPress of an edit control:

` `**if** Key **in** ['a'..'z'] + ['A'..'Z'] **then** Key := #0

### Sets with Enumerations

A commonly used scenario in Delphi code is to mix both enumerated types and set types.

Here's an example:

` `**type**
TWorkDay = (Monday, Tuesday, Wednesday, Thursday, Friday) ;
TDaySet = **set of** TWorkDay;
**var**
days : TDaySet;
**begin**
days := [Monday, Friday];
days := days + [Tuesday, Thursday] - [Friday];
**if** Wednesday **IN** days **then** ShowMessage('I love Wednesday!') ;

Question: will the message be displayed? Answer: no :(

### Sets in Delphi Control Properties

When you need to apply "bold" to the font used in TEdit controls, you either use the Object Inspector or the following code:

```
Font.Style := Font.Style + [fsBold];
```

The Font's Style property is a set type property! Here's how it is defined:

```
type
TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut) ;
TFontStyles =
```**set of** TFontStyle;
...
**property** Style: TFontStyles ...

So, an enumerated type TFontStyle is used as the base type for the set type TFontStyles. The Style property of the TFont class is of type TFontStyles - therefore a set type property.

Another example includes the result of the MessageDlg function. A MessageDlg function is used to bring up a message box and obtain the user's response. One of the parameters of the function is the Buttons parameter of type TMsgDlgButtons.

TMsgDlgButtons is defined as a set of (mbYes, mbNo, mbOK, mbCancel, mbAbort, mbRetry, mbIgnore, mbAll, mbNoToAll, mbYesToAll, mbHelp).

If you display a message to the user containing Yes, OK and Cancel buttons and you want to execute some code if either the Yes or Ok buttons were clicked you can use the next code:

` `**if** MessageDlg('Learning about Sets!', mtInformation, [mbYes, mbOk, mbCancel], 0) **in** [mrYes, mrOK] **then** ...

Final word: sets are great. Sets might appear confusing to a Delphi beginner, but as soon as you start using set type variables you will find out they provide much more then it sounded in the beginning. At least I have :))