1.1.0 • Published 3 years ago

enumerated_types v1.1.0

Weekly downloads
-
License
BSD-3-Clause
Repository
github
Last release
3 years ago

Table of Contents

  1. Enumerated Types for Javascript
    1. Getting Started
      1. Installing
    2. Usage
      1. Enumerated constants with Enum
      2. Enumerated constants with EnumObjects
      3. Binary options with Flags
      4. Constrained values with EnumeratedRange

Enumerated Types for Javascript

Full featured, extensible enumerations for javascript.

Getting Started

Installing

  1. Node

    npm install enumerated_types
  2. Browser

    Download the package from here.

    The js folder contains two files: enumerated_types.js and enumerated_module.js.

    To use enumerated_types.js load it from an HTML script tag:

    <script type='text/javascript' src='/js/enumerated_types.js'></script>

    To use enumerated_module.js import the required objects/functions:

    import * as enumeration from '/js/enumerated_module.js'
    import {Enum} from '/js/enumerated_module.js'

Usage

The enumeration package provides a base object, EnumerationBase, on which to base enumerated types.

The following methods are available on all enumerated types:

When creating your own enumerated types, the methods marks "no" under 'Default Implementation' must be implemented. All other methods may be overridden if required.

Enumerated constants with Enum

Use the Enum function to create a set of constants:

Color = Enum(
    'RED',
    'ORANGE',
    'YELLOW',
    'GREEN',
    'BLUE',
    'INDIGO',
    'VIOLET'
)

Constants can be converted to and from their interger equivalents:

Color.fromInt(3)    // Color.GREEN
Color.GREEN.toInt() // 3

And you can also get the string representation:

Color.RED.toString()    // 'RED'
Color.VIOLET.toString() // 'VIOLET'

This allows you to use the constants as if they were object keys.

someObj = {
    Color.RED : 'Something having to do with red.',
    Color.BLUE : 'Something to do with blue.',
}

In the above example the actual keys are the strings RED and BLUE because javascript calls toString() automatically when an object is used as a key.

You can navigate forwards and backwards through the sequence using next() and previous(). Going past the end of the sequence throws and error:

Color.RED.next()        // Color.ORANGE
Color.INDIGO.previous() // Color.BLUE
Color.RED.previous()    // Error!
Color.VIOLET.next()     // Error!

To get around that you can get an iterator() and loop through the values with a for loop or with forEach.

// Prints all the colors to the console.
for (let color of Color.iterator()) {
    ; console.log(color.toString()) 
}

// Also prints all the colors to the console.
Color.iterator().forEach(color => console.log(color.toString()))

The iterator() method takes two parameters from and to, both optional, so you can iterate though a subset of the values.

Color.iterator(Color.GREEN, Color.INDIGO)
    .forEach(color => console.log(color.toString()))

And can even be used to iterate though the values in reverse order if desired.

Color.iterator(Color.last(), Color.first())
    .forEach(color => console.log(color.toString()))

The constants can be compared in a number of ways.

Color.RED.isLess(Color.BLUE)                // true
Color.VIOLET.isLess(Color.BLUE)             // false
Color.VIOLET.isGreater(Color.BLUE)          // true
Color.VIOLET.isGreater(Color.VIOLET)        // false
Color.VIOLET.isGreaterOrEqual(Color.VIOLET) // true
Color.RED.isEqual(Color.GREEN)              // false
Color.RED.isEqual(Color.RED)                // true
Color.RED === Color.GREEN                   // false
Color.RED === Color.RED                     // true

Finally, the type can be extended with addtional methods:

Answers = Enum('YES', 'NO', 'MAYBE').methods({
    toLowerCase() {
	return this.toString().toLowerCase();
    },
    capitalize() {
	const as_string = this.toString();
	const first = as_string[0];
	const rest = as_string.substring(1).toLowerCase();
	return `${first}${rest}`;
    }
})

console.log(Answers.YES.toLowerCase());  // yes
console.log(Answers.Maybe.capitalize()); // Maybe

You can call methods at most once. After that the prototype object is frozen and can't be modified further.

Enumerated constants with EnumObjects

The EnumObjects function works in much the same way as Enum but instead of a list of strings it takes an object as input. Each key becomes a constant and, unlike Enum each contant can have its own additional properties and methods.

Animal = E.EnumObjects({
    DOG : {
	speak() {
	    console.log('woof!');
	}
    },
    CAT : {
	speak() {
	    console.log('meow!');
	}
    },
    GIRAFFE : {}
}).methods({
    speak() {
	console.log('...');
    }
})

Animal.DOG.speak()     // woof!
Animal.CAT.speak()     // meow!
Animal.GIRAFFE.speak() // ...
Animal.iterator().forEach(a => a.speak())

In particular, notice that we can use methods to define default methods which will apply to all constants that don't specifically override it: DOG and CAT use their own behaviour while GIRAFFE uses the default.

Binary options with Flags

Flags are like enumerated constants except, instead of being sequential, each flag's value is a power of 2.

Options = Flags('A', 'B', 'C', 'D')
Options.A.toInt() // 1
Options.B.toInt() // 2
Options.C.toInt() // 4
Options.D.toInt() // 8

This means you can combine multiple flags into a single integer value using the bitwise OR (|) operator:

Options.A | Options.B | Options.D;  // 11

Flag types have an additional method, listFromInt(), which returns a list of all options represented by a given integer.

Options.listFromInt(Options.A | Options.B | Options.D);  // [Options.A, Options.B, Options.D]
Options.listFromInt(12)                                  // [Options.C, Options.D]
Options.listFromInt(0)                                   // []
Options.listFromInt(16)                                  // Error! Out of range.

Constrained values with EnumeratedRange

Another type of enumeration are values constrained to a particular range: values between 1 and 10, for instance. In this case it's actually the number we're interested in not a symbolic name and there are, generally, too many possible values to create them as contants. Instead we can create an EnumeratedRange.

SmallNum = EnumeratedRange(1, 10, 'SmallNum')

The resulting value, SmallNum in this example, can then be used to construct values in this range. The third parameter is optional and is only relevant when calling toString(). Trying to create a value outside of the allowed range throws an error.

x = SmallNum(1)
y = SmallNum(5)
z = SmallNum(10)
error1 = SmallNum(0)  // Error!
error2 = SmallNum(11) // Error!

Even though the values don't exist as constants you can still get an iterator over all the possible values:

// Prints all SmallNum values from 1 to 10
SmallNum.iterator().forEach(n => console.log(n))