Defining enumerations is directly supported in information modeling languages (such as in UML Class Diagrams), in data schema languages (such as in XML Schema, but not in SQL), and in many programming languages (such as in C++ and Java, but not in JavaScript).
Unfortunately, standard SQL does not support enumerations. Some DBMS, such as MySQL and Postgres, provide their own extensions of SQL column definitions in the CREATE TABLE statement allowing to define enumeration-valued columns.
A MySQL
enumeration is specified as a list of enumeration labels with the
keyword ENUM
within a column definition, like
so:
CREATE TABLE people ( name VARCHAR(40), gender ENUM('MALE', 'FEMALE', 'UNDETERMINED') );
A Postgres enumeration is specified as a special user-defined type that can be used in columns definitions:
CREATE TYPE GenderEL AS ENUM ('MALE', 'FEMALE', 'UNDETERMINED'); CREATE TABLE people ( name text, gender GenderEL )
In XML Schema, an enumeration datatype can be defined as a simple
type restricting the primitive type xs:string
in the
following way:
<xs:simpleType name="BookCategoryEL"> <xs:restriction base="xs:string"> <xs:enumeration value="NOVEL"/> <xs:enumeration value="BIOGRAPHY"/> <xs:enumeration value="TEXTBOOK"/> <xs:enumeration value="OTHER"/> </xs:restriction> </xs:simpleType>
In JavaScript, we can define an enumeration as a special JS object
having a property for each enumeration literal such that the property's
name is the enumeration literal's name (the enumeration label or code in
upper case) and its value is the corresponding enumeration index. One
approach for implementing this is using the
Object.defineProperties
method:
var BookCategoryEL = null; Object.defineProperties( BookCategoryEL, { NOVEL: {value: 1, writable: false}, BIOGRAPHY: {value: 2, writable: false}, TEXTBOOK: {value: 3, writable: false}, OTHER: {value: 4, writable: false}, MAX: {value: 4, writable: false}, labels: {value:["novel","biography","textbook","other"], writable: false} });
This definition allows using the enumeration literals
BookCategoryEL.NOVEL
, BookCategoryEL.BIOGRAPHY
etc., standing for the enumeration indexes 1, 2 , 3 and 4, in program
statements. Notice how this definition takes care of the requirement
that enumeration literals like BookCategoryEL.NOVEL
are
constants, the value of which cannot be changed during program
execution. This is achieved with the help of the property descriptor
writable: false
in the Object.defineProperties
statement.
We can also use a more generic approach and define a meta-class
Enumeration
for creating enumerations in the form of
special JS objects:
function Enumeration( enumLabels) { var i=0, LBL=""; this.MAX = enumLabels.length; this.labels = enumLabels; // generate the enum literals as capitalized keys/properties for (i=1; i <= enumLabels.length; i++) { LBL = enumLabels[i-1].toUpperCase(); this[LBL] = i; } // prevent any runtime change to the enumeration Object.freeze( this); };
Using this Enumeration
class allows to define
a new enumeration in the following way:
var BookCategoryEL = new Enumeration(["novel","biography","textbook","other"])
Having an enumeration like BookCategoryEL
, we can
then check if an enumeration attribute like category
has an
admissible value by testing if its value is not smaller than 1 and not
greater than BookCategoryEL.MAX
. Also, the label can be
retrieved in the following way:
formEl.category.value = BookCategoryEL.labels[this.category - 1];
As an example, we consider the following model class
Book
with the enumeration attribute
category
:
function Book( slots) { this.isbn = ""; // string this.title = ""; // string this.category = 0; // number (BookCategoryEL) if (arguments.length > 0) { this.setIsbn( slots.isbn); this.setTitle( slots.title); this.setCategory( slots.category); } };
For validating input values for the enumeration attribute
category
, we can use the following check function:
Book.checkCategory = function (c) { if (!c) { return new MandatoryValueConstraintViolation( "A category must be provided!"); } else if (!Number.isInteger(c) || c < 1 || c > BookCategoryEL.MAX) { return new RangeConstraintViolation( "The category must be a positive integer " + "not greater than "+ BookCategoryEL.MAX +" !"); } else { return new NoConstraintViolation(); } };
Notice how the range constraint defined by the enumeration
BookCategoryEL
is checked: it is tested if the input value
c
is a positive integer and if it is not greater than
BookCategoryEL.MAX
.