enumerated
v1.0.3
Published
A simple, lightweight, easy-to-use and high performance implementation of enumerated type for JavaScript.
Downloads
21
Maintainers
Readme
Enumerated
What's Enumerated?
A simple, lightweight, easy-to-use and high performance implementation of Enum type for JavaScript. Enumerated uses lookup tables and bit operators internally, so is blazingly fast and also provides a friendly interface to reduce the time you spend on exploring and integrating the module. It's really lightweight - ~420 sloc excluding comments - but contains all the usual features you need and works in modern browsers (IE8+) too.
Usage
var Enum = require('enumerated')
var colors = new Enum('red', 'green', 'blue')
var selectedColors = colors.red | colors.blue
colors.valuesOf(selectedColors) // [ 'red', 'blue' ]
var numbers = Enum({
one: 1,
two: 2,
three: 3,
four: 4,
five: 5
})
var selectedNumbers = numbers.four | numbers.two
numbers.valuesOf(selectedNumbers) // [ 2, 4 ]
selectedNumbers |= numbers.five // extend selectedNumbers with another value
numbers.keysOf(selectedNumbers) // [ 'two', 'four', 'five' ]
selectedNumbers ^= numbers.two // remove one item from selectedNumbers
numbers.valuesOf(selectedNumbers) // [ 4, 5 ]
selectedNumbers ^= numbers.five | numbers.four // remove two items in one step
numbers.valuesOf(selectedNumbers) // []
// not flaggable enum
var states = Enum('pending', 'processing', 'finished', 'failed', { single: true })
var selectedState = states.processing
states.valueOf(selectedState) // 'processing'
var selectedStates = states.processing | states.pending
try {
states.valuesOf(selectedStates)
}
catch(ex) {
// throws "Error: This enum allows only one single choice."
}
// make it case insensitive
var days = Enum('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', { ignoreCase: true })
days.get('monday') // { key: 'monday', value: 'Monday' }
days.get('fRiDaY') // { key: 'friday', value: 'Friday' }
// items will be accessible lowercased with the dot or index operator if ignoreCase option is set to true
var selectedDays = days.saturday | days['sunday']
// ...
If you don't want to require Enumerated again and again in different modules, but instead only once in the main module, do the following:
require('enumerated').global = true
// after that point Enum constructor has been exposed to the global context,
// so feel free to use it anywhere
var animals = Enum({
eagle: 'bird',
snake: 'reptile',
cat: 'four-footed'
})
// ...
Examples of comparisons:
var foodList = [ 'pizza', 'hamburger', 'macaroni', 'ketchup' ],
food = Enum(foodList)
var dinner = food.pizza | food.ketchup
Enum.item(food.pizza).in(dinner) // true
Enum.item(food.hamburger).in(dinner) // false
food.item('pizza').in(dinner) // true
food.item('hamburger').in(dinner) // false
dinner === food.ketchup | food.pizza // true
dinner === food.ketchup | food.hamburger // false
dinner === food.pizza // false
var food2 = Enum('pizza', 'hamburger', 'macaroni', 'ketchup')
food == food2 // false, since object is a reference type
food.pizza === food2.pizza // true, because under the hood Enum uses simple integers to mark items
food instanceof Enum // true
food.pizza instanceof Enum // false
typeof food === 'object' // true
typeof food.pizza === 'number' // true
Supported constructors:
var a = Enum('a', 'b', 'c'),
b = Enum([ 'a', 'b', 'c' ]), // equivalent with the previous
c = new Enum({ a: 1, b: 2, c: 3 }), // new operator is optional
d = Enum('a', 'b', 'c', { ignoreCase: true /* default: false */ }), // you can pass an object containing the desired options as the last argument
e = Enum([ 'a', 'b', 'c' ], { single: true /* default: false */ }) // usually the second argument will be the last :)
Examples of useful Enum instance members:
var fruitNames = { apple: 'red', orange: 'orange', grape: 'green', banana: 'yellow', pineapple: 'brown' },
fruits = Enum(fruitNames),
selected = fruits.apple | fruits.banana
fruits.valueOf(selected) // 'red', because it returns the first selected item
fruits.valuesOf(selected) // [ 'red', 'yellow' ]
fruits.keyOf(selected) // 'apple'
fruits.keysOf(selected) // [ 'apple', 'banana' ]
fruits.get('grape') // { key: 'grape', value: 'green' }
fruits.get(3) // { key: 'banana', value: 'yellow' }
fruits.valueByKey('grape') // 'green'
fruits.valuesByKeys('apple', 'pineapple') // [ 'red', 'brown' ]
fruits.keyByValue('yellow') // 'banana'
fruits.keysByValues([ 'orange', 'green' ]) // [ 'orange', 'grape' ], note that you also can pass an array as well to all the member functions expecting multiple parameters
fruits.item('apple').in(selected) // true
fruits.item('orange').in(selected) // false
fruits.item('fake').in(selected) // false
fruits.length // 5
fruits.keys // [ 'apple', 'orange', 'grape', 'banana', 'pineapple' ]
fruits.values // [ 'red', 'orange', 'green', 'yellow', 'brown' ]
fruits.items /*
[
{ key: 'apple', value: 'red' },
{ key: 'orange', value: 'orange' },
{ key: 'grape', value: 'green' },
{ key: 'banana', value: 'yellow' },
{ key: 'pineapple', value: 'brown' }
]
*/
fruits.toJSON() // { descriptor: { apple: 'red', orange: 'orange', grape: 'green', banana: 'yellow', pineapple: 'brown' }, options: { single: false, ignoreCase: false } }
fruits.toJSON(selected) // [ 'red', 'yellow' ]
JSON.stringify(fruits) // '{"descriptor":{"apple":"red","orange":"orange","grape":"green","banana":"yellow","pineapple":"brown"},"options":{"single":false,"ignoreCase":false}}'
fruits.fromJSON('["red","yellow"]') === selected // true
console.log(fruits) /*
Enum({
"apple": "red",
"orange": "orange",
"grape": "green",
"banana": "yellow",
"pineapple": "brown"
})
*/
Examples of useful Enum "static" members:
var fruits = Enum.fromJSON('{"descriptor":{"apple":"red","orange":"orange","grape":"green","banana":"yellow","pineapple":"brown"},"options":{"single":false,"ignoreCase":false}}')
Enum.global = true // set it true to expose Enum constructor into the global context
Enum.item(fruits.apple).in(fruits.banana | fruits.apple) // true
Enum.item(fruits.apple).in(fruits.orange | fruits.pineapple) // false
Enum.item(fruits.apple).in(fruits.apple) // true
Enum.item(fruits.apple).in(fruits.pineapple) // false
Enum.item(fruits.apple).isSingle() // true
Enum.item(fruits.apple).isMultiple() // false
Enum.item(fruits.apple | fruits.orange).isSingle() // false
Enum.item(fruits.apple | fruits.orange).isMultiple() // true
Enum.MAX_LENGTH // 31 or 63, the maximum count of items in an Enum instance. depends on the integer size of the system
And there is some extra sugar:
// for non-flaggable enums you can use the language's built-in switch statement
var state = Enum('pending', 'finished', { single: true }),
current = state.pending
switch(current) {
case state.pending:
// ...
break
case state.finished:
// ...
break
default:
// ...
break
}
// unfortunately you can't do the same with flagged enums,
// but Enumerated is shipped with a handy helper method
var fruits = Enum('apple', 'orange', 'strawberry', 'lemon', 'banana'),
likedFruits = fruits.apple | fruits.strawberry | fruits.banana
function printCase(value) {
console.log('user likes', value)
}
fruits.switch(likedFruits)
.case('apple', printCase)
.case('orange', printCase)
.case('strawberry', printCase)
.case('lemon', printCase)
.case('banana', printCase)
/*
output:
user likes apple
user likes strawberry
user likes banana
*/
Note: Proper examples will be added soon to the examples folder.
Installation
With npm:
npm install --save enumerated
With git:
git clone git://github.com/schwarzkopfb/enumerated.git
cd enumerated
npm test
Browser usage
If you want to use Enumerated in a browser, just download and include enumerated.min.js in your front-end project. Don't worry, it weights only about 5 kB.
Documentation
The complete documentation with examples for all the instance and static members can be found here.
AMD (Requirejs) support
It's supported to load Enumerated with a module loader that follows the AMD pattern, like the deservedly popular Requirejs.
requirejs([ 'enumerated' ], function(Enum) {
var mouseButtons = Enum('left', 'middle', 'right')
// ...
})
Unit tests
The project is entirely covered with unit tests. Of course you can npm test
to run them in Node.js environment or click here if you're curious about the results in a web browser.
Benchmark
Performance is important, so I wrote a simple benchmark for the project. The results on my MacBook Pro (Mid 2010) are the following:
- 50000000 get() calls performed in 1.36 seconds (36764705.88 ops/sec)
- 50000000 valueOf() calls performed in 2.88 seconds (17361111.11 ops/sec)
- 50000000 valuesOf() calls performed in 9.27 seconds (5393743.26 ops/sec)
- 1000000 fromValues() calls performed in 3.84 seconds (260416.67 ops/sec)
- 1000000 valuesByKeys() calls performed in 3.90 seconds (256410.26 ops/sec)
- 100000 constructor calls performed in 5.77 seconds (17331.02 ops/sec)
In the light of the results, the overhead compared to more primitive solutions is negligible.
You can run the benchmark on your machine with the following command:
npm run benchmark