Read text from the run command [duplicate] - javascript
I have a web server written in Node.js and I would like to launch with a specific folder. I'm not sure how to access arguments in JavaScript. I'm running node like this:
$ node server.js folder
here server.js is my server code. Node.js help says this is possible:
$ node -h
Usage: node [options] script.js [arguments]
How would I access those arguments in JavaScript? Somehow I was not able to find this information on the web.
Standard Method (no library)
The arguments are stored in process.argv
Here are the node docs on handling command line args:
process.argv is an array containing the command line arguments. The first element will be 'node', the second element will be the name of the JavaScript file. The next elements will be any additional command line arguments.
// print process.argv
process.argv.forEach(function (val, index, array) {
console.log(index + ': ' + val);
});
This will generate:
$ node process-2.js one two=three four
0: node
1: /Users/mjr/work/node/process-2.js
2: one
3: two=three
4: four
To normalize the arguments like a regular javascript function would receive, I do this in my node.js shell scripts:
var args = process.argv.slice(2);
Note that the first arg is usually the path to nodejs, and the second arg is the location of the script you're executing.
The up-to-date right answer for this it to use the minimist library. We used to use node-optimist but it has since been deprecated.
Here is an example of how to use it taken straight from the minimist documentation:
var argv = require('minimist')(process.argv.slice(2));
console.dir(argv);
-
$ node example/parse.js -a beep -b boop
{ _: [], a: 'beep', b: 'boop' }
-
$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
{ _: [ 'foo', 'bar', 'baz' ],
x: 3,
y: 4,
n: 5,
a: true,
b: true,
c: true,
beep: 'boop' }
2018 answer based on current trends in the wild:
Vanilla javascript argument parsing:
const args = process.argv;
console.log(args);
This returns:
$ node server.js one two=three four
['node', '/home/server.js', 'one', 'two=three', 'four']
Official docs
Most used NPM packages for argument parsing:
Minimist: For minimal argument parsing.
Commander.js: Most adopted module for argument parsing.
Meow: Lighter alternative to Commander.js
Yargs: More sophisticated argument parsing (heavy).
Vorpal.js: Mature / interactive command-line applications with argument parsing.
Optimist (node-optimist)
Check out optimist library, it is much better than parsing command line options by hand.
Update
Optimist is deprecated. Try yargs which is an active fork of optimist.
Several great answers here, but it all seems very complex. This is very similar to how bash scripts access argument values and it's already provided standard with node.js as MooGoo pointed out.
(Just to make it understandable to somebody that's new to node.js)
Example:
$ node yourscript.js banana monkey
var program_name = process.argv[0]; //value will be "node"
var script_path = process.argv[1]; //value will be "yourscript.js"
var first_value = process.argv[2]; //value will be "banana"
var second_value = process.argv[3]; //value will be "monkey"
No Libs with Flags Formatted into a Simple Object
function getArgs () {
const args = {};
process.argv
.slice(2, process.argv.length)
.forEach( arg => {
// long arg
if (arg.slice(0,2) === '--') {
const longArg = arg.split('=');
const longArgFlag = longArg[0].slice(2,longArg[0].length);
const longArgValue = longArg.length > 1 ? longArg[1] : true;
args[longArgFlag] = longArgValue;
}
// flags
else if (arg[0] === '-') {
const flags = arg.slice(1,arg.length).split('');
flags.forEach(flag => {
args[flag] = true;
});
}
});
return args;
}
const args = getArgs();
console.log(args);
Examples
Simple
input
node test.js -D --name=Hello
output
{ D: true, name: 'Hello' }
Real World
input
node config/build.js -lHRs --ip=$HOST --port=$PORT --env=dev
output
{
l: true,
H: true,
R: true,
s: true,
ip: '127.0.0.1',
port: '8080',
env: 'dev'
}
Commander.js
Works great for defining your options, actions, and arguments. It also generates the help pages for you.
Promptly
Works great for getting input from the user, if you like the callback approach.
Co-Prompt
Works great for getting input from the user, if you like the generator approach.
Stdio Library
The easiest way to parse command-line arguments in NodeJS is using the stdio module. Inspired by UNIX getopt utility, it is as trivial as follows:
var stdio = require('stdio');
var ops = stdio.getopt({
'check': {key: 'c', args: 2, description: 'What this option means'},
'map': {key: 'm', description: 'Another description'},
'kaka': {args: 1, required: true},
'ooo': {key: 'o'}
});
If you run the previous code with this command:
node <your_script.js> -c 23 45 --map -k 23 file1 file2
Then ops object will be as follows:
{ check: [ '23', '45' ],
args: [ 'file1', 'file2' ],
map: true,
kaka: '23' }
So you can use it as you want. For instance:
if (ops.kaka && ops.check) {
console.log(ops.kaka + ops.check[0]);
}
Grouped options are also supported, so you can write -om instead of -o -m.
Furthermore, stdio can generate a help/usage output automatically. If you call ops.printHelp() you'll get the following:
USAGE: node something.js [--check <ARG1> <ARG2>] [--kaka] [--ooo] [--map]
-c, --check <ARG1> <ARG2> What this option means (mandatory)
-k, --kaka (mandatory)
--map Another description
-o, --ooo
The previous message is shown also if a mandatory option is not given (preceded by the error message) or if it is mispecified (for instance, if you specify a single arg for an option and it needs 2).
You can install stdio module using NPM:
npm install stdio
If your script is called myScript.js and you want to pass the first and last name, 'Sean Worthington', as arguments like below:
node myScript.js Sean Worthington
Then within your script you write:
var firstName = process.argv[2]; // Will be set to 'Sean'
var lastName = process.argv[3]; // Will be set to 'Worthington'
Simple + ES6 + no-dependency + supports boolean flags
const process = require( 'process' );
const argv = key => {
// Return true if the key exists and a value is defined
if ( process.argv.includes( `--${ key }` ) ) return true;
const value = process.argv.find( element => element.startsWith( `--${ key }=` ) );
// Return null if the key does not exist and a value is not defined
if ( !value ) return null;
return value.replace( `--${ key }=` , '' );
}
Output:
If invoked with node app.js then argv('foo') will return null
If invoked with node app.js --foo then argv('foo') will return true
If invoked with node app.js --foo= then argv('foo') will return ''
If invoked with node app.js --foo=bar then argv('foo') will return 'bar'
command-line-args is worth a look!
You can set options using the main notation standards (learn more). These commands are all equivalent, setting the same values:
$ example --verbose --timeout=1000 --src one.js --src two.js
$ example --verbose --timeout 1000 --src one.js two.js
$ example -vt 1000 --src one.js two.js
$ example -vt 1000 one.js two.js
To access the values, first create a list of option definitions describing the options your application accepts. The type property is a setter function (the value supplied is passed through this), giving you full control over the value received.
const optionDefinitions = [
{ name: 'verbose', alias: 'v', type: Boolean },
{ name: 'src', type: String, multiple: true, defaultOption: true },
{ name: 'timeout', alias: 't', type: Number }
]
Next, parse the options using commandLineArgs():
const commandLineArgs = require('command-line-args')
const options = commandLineArgs(optionDefinitions)
options now looks like this:
{
src: [
'one.js',
'two.js'
],
verbose: true,
timeout: 1000
}
Advanced usage
Beside the above typical usage, you can configure command-line-args to accept more advanced syntax forms.
Command-based syntax (git style) in the form:
$ executable <command> [options]
For example.
$ git commit --squash -m "This is my commit message"
Command and sub-command syntax (docker style) in the form:
$ executable <command> [options] <sub-command> [options]
For example.
$ docker run --detached --image centos bash -c yum install -y httpd
Usage guide generation
A usage guide (typically printed when --help is set) can be generated using command-line-usage. See the examples below and read the documentation for instructions how to create them.
A typical usage guide example.
The polymer-cli usage guide is a good real-life example.
Further Reading
There is plenty more to learn, please see the wiki for examples and documentation.
Here's my 0-dep solution for named arguments:
const args = process.argv
.slice(2)
.map(arg => arg.split('='))
.reduce((args, [value, key]) => {
args[value] = key;
return args;
}, {});
console.log(args.foo)
console.log(args.fizz)
Example:
$ node test.js foo=bar fizz=buzz
bar
buzz
Note: Naturally this will fail when the argument contains a =. This is only for very simple usage.
There's an app for that. Well, module. Well, more than one, probably hundreds.
Yargs is one of the fun ones, its docs are cool to read.
Here's an example from the github/npm page:
#!/usr/bin/env node
var argv = require('yargs').argv;
console.log('(%d,%d)', argv.x, argv.y);
console.log(argv._);
Output is here (it reads options with dashes etc, short and long, numeric etc).
$ ./nonopt.js -x 6.82 -y 3.35 rum
(6.82,3.35)
[ 'rum' ]
$ ./nonopt.js "me hearties" -x 0.54 yo -y 1.12 ho
(0.54,1.12)
[ 'me hearties', 'yo', 'ho' ]
proj.js
for(var i=0;i<process.argv.length;i++){
console.log(process.argv[i]);
}
Terminal:
nodemon app.js "arg1" "arg2" "arg3"
Result:
0 'C:\\Program Files\\nodejs\\node.exe'
1 'C:\\Users\\Nouman\\Desktop\\Node\\camer nodejs\\proj.js'
2 'arg1' your first argument you passed.
3 'arg2' your second argument you passed.
4 'arg3' your third argument you passed.
Explaination:
The directory of node.exe in your machine (C:\Program Files\nodejs\node.exe)
The directory of your project file
(proj.js)
Your first argument to node (arg1)
Your second argument to node (arg2)
Your third argument to node (arg3)
your actual arguments start form second index of argv array, that is process.argv[2].
whithout librairies: using Array.prototype.reduce()
const args = process.argv.slice(2).reduce((acc, arg) => {
let [k, v = true] = arg.split('=')
acc[k] = v
return acc
}, {})
for this command node index.js count=2 print debug=false msg=hi
console.log(args) // { count: '2', print: true, debug: 'false', msg: 'hi' }
also,
we can change
let [k, v = true] = arg.split('=')
acc[k] = v
by (much longer)
let [k, v] = arg.split('=')
acc[k] = v === undefined ? true : /true|false/.test(v) ? v === 'true' : /[\d|\.]+/.test(v) ? Number(v) : v
to auto parse Boolean & Number
console.log(args) // { count: 2, print: true, debug: false, msg: 'hi' }
Parsing argument based on standard input ( --key=value )
const argv = (() => {
const arguments = {};
process.argv.slice(2).map( (element) => {
const matches = element.match( '--([a-zA-Z0-9]+)=(.*)');
if ( matches ){
arguments[matches[1]] = matches[2]
.replace(/^['"]/, '').replace(/['"]$/, '');
}
});
return arguments;
})();
Command example
node app.js --name=stackoverflow --id=10 another-argument --text="Hello World"
Result of argv: console.log(argv)
{
name: "stackoverflow",
id: "10",
text: "Hello World"
}
Passing,parsing arguments is an easy process. Node provides you with the process.argv property, which is an array of strings, which are the arguments that were used when Node was invoked.
The first entry of the array is the Node executable, and the second entry is the name of your script.
If you run script with below atguments
$ node args.js arg1 arg2
File : args.js
console.log(process.argv)
You will get array like
['node','args.js','arg1','arg2']
In the node code require the built in process lib.
const {argv} = require('process')
Run the program with their arguments.
$ node process-args.js one two=three four
argv is the array that follows:
argv[0] = /usr/bin/node
argv[1] = /home/user/process-args.js
argv[2] = one
argv[3] = two=three
argv[4] = four
npm install ps-grab
If you want to run something like this :
node greeting.js --user Abdennour --website http://abdennoor.com
--
var grab=require('ps-grab');
grab('--username') // return 'Abdennour'
grab('--action') // return 'http://abdennoor.com'
Or something like :
node vbox.js -OS redhat -VM template-12332 ;
--
var grab=require('ps-grab');
grab('-OS') // return 'redhat'
grab('-VM') // return 'template-12332'
You can parse all arguments and check if they exist.
file: parse-cli-arguments.js:
module.exports = function(requiredArguments){
var arguments = {};
for (var index = 0; index < process.argv.length; index++) {
var re = new RegExp('--([A-Za-z0-9_]+)=([A/-Za-z0-9_]+)'),
matches = re.exec(process.argv[index]);
if(matches !== null) {
arguments[matches[1]] = matches[2];
}
}
for (var index = 0; index < requiredArguments.length; index++) {
if (arguments[requiredArguments[index]] === undefined) {
throw(requiredArguments[index] + ' not defined. Please add the argument with --' + requiredArguments[index]);
}
}
return arguments;
}
Than just do:
var arguments = require('./parse-cli-arguments')(['foo', 'bar', 'xpto']);
You can reach command line arguments using system.args. And i use the solution below to parse arguments into an object, so i can get which one i want by name.
var system = require('system');
var args = {};
system.args.map(function(x){return x.split("=")})
.map(function(y){args[y[0]]=y[1]});
now you don't need to know the index of the argument. use it like args.whatever
Note: you should use named arguments like file.js x=1 y=2 to use
this solution.
Passing arguments is easy, and receiving them is just a matter of reading the process.argv array Node makes accessible from everywhere, basically. But you're sure to want to read them as key/value pairs, so you'll need a piece to script to interpret it.
Joseph Merdrignac posted a beautiful one using reduce, but it relied on a key=value syntax instead of -k value and --key value. I rewrote it much uglier and longer to use that second standard, and I'll post it as an answer because it wouldn't fit as a commentary. But it does get the job done.
const args = process.argv.slice(2).reduce((acc,arg,cur,arr)=>{
if(arg.match(/^--/)){
acc[arg.substring(2)] = true
acc['_lastkey'] = arg.substring(2)
} else
if(arg.match(/^-[^-]/)){
for(key of arg.substring(1).split('')){
acc[key] = true
acc['_lastkey'] = key
}
} else
if(acc['_lastkey']){
acc[acc['_lastkey']] = arg
delete acc['_lastkey']
} else
acc[arg] = true
if(cur==arr.length-1)
delete acc['_lastkey']
return acc
},{})
With this code a command node script.js alpha beta -charlie delta --echo foxtrot would give you the following object
args = {
"alpha":true,
"beta":true,
"c":true,
"h":true,
"a":true,
"r":true
"l":true,
"i":true,
"e":"delta",
"echo":"foxtrot"
}
Although Above answers are perfect, and someone has already suggested yargs, using the package is really easy.
This is a nice package which makes passing arguments to command line really easy.
npm i yargs
const yargs = require("yargs");
const argv = yargs.argv;
console.log(argv);
Please visit https://yargs.js.org/ for more info.
TypeScript solution with no libraries:
interface IParams {
[key: string]: string
}
function parseCliParams(): IParams {
const args: IParams = {};
const rawArgs = process.argv.slice(2, process.argv.length);
rawArgs.forEach((arg: string, index) => {
// Long arguments with '--' flags:
if (arg.slice(0, 2).includes('--')) {
const longArgKey = arg.slice(2, arg.length);
const longArgValue = rawArgs[index + 1]; // Next value, e.g.: --connection connection_name
args[longArgKey] = longArgValue;
}
// Shot arguments with '-' flags:
else if (arg.slice(0, 1).includes('-')) {
const longArgKey = arg.slice(1, arg.length);
const longArgValue = rawArgs[index + 1]; // Next value, e.g.: -c connection_name
args[longArgKey] = longArgValue;
}
});
return args;
}
const params = parseCliParams();
console.log('params: ', params);
Input: ts-node index.js -p param --parameter parameter
Output: { p: 'param ', parameter: 'parameter' }
Without libraries
If you want to do this in vanilla JS/ES6 you can use the following solution
worked only in NodeJS > 6
const args = process.argv
.slice(2)
.map((val, i)=>{
let object = {};
let [regexForProp, regexForVal] = (() => [new RegExp('^(.+?)='), new RegExp('\=(.*)')] )();
let [prop, value] = (() => [regexForProp.exec(val), regexForVal.exec(val)] )();
if(!prop){
object[val] = true;
return object;
} else {
object[prop[1]] = value[1] ;
return object
}
})
.reduce((obj, item) => {
let prop = Object.keys(item)[0];
obj[prop] = item[prop];
return obj;
}, {});
And this command
node index.js host=http://google.com port=8080 production
will produce the following result
console.log(args);//{ host:'http://google.com',port:'8080',production:true }
console.log(args.host);//http://google.com
console.log(args.port);//8080
console.log(args.production);//true
p.s. Please correct the code in map and reduce function
if you find more elegant solution, thanks ;)
The simplest way of retrieving arguments in Node.js is via the process.argv array. This is a global object that you can use without importing any additional libraries to use it. You simply need to pass arguments to a Node.js application, just like we showed earlier, and these arguments can be accessed within the application via the process.argv array.
The first element of the process.argv array will always be a file system path pointing to the node executable. The second element is the name of the JavaScript file that is being executed. And the third element is the first argument that was actually passed by the user.
'use strict';
for (let j = 0; j < process.argv.length; j++) {
console.log(j + ' -> ' + (process.argv[j]));
}
All this script does is loop through the process.argv array and prints the indexes, along with the elements stored in those indexes. It's very useful for debugging if you ever question what arguments you're receiving, and in what order.
You can also use libraries like yargs for working with commnadline arguments.
You can get command-line information from process.argv()
And I don't want to limit the problem to node.js. Instead, I want to turn it into how to parse the string as the argument.
console.log(ArgumentParser(`--debug --msg="Hello World" --title="Test" --desc=demo -open --level=5 --MyFloat=3.14`))
output
{
"debug": true,
"msg": "Hello World",
"title": "Test",
"desc": "demo",
"open": true,
"level": 5,
"MyFloat": 3.14
}
code
Pure javascript, no dependencies needed
// ๐ Below is Test
(() => {
window.onload = () => {
const testArray = [
`--debug --msg="Hello World" --title="Test" --desc=demo -open --level=5 --MyFloat=3.14`,
]
for (const testData of testArray) {
try {
const obj = ArgumentParser(testData)
console.log(obj)
} catch (e) {
console.error(e.message)
}
}
}
})()
// ๐ Script
class ParserError extends Error {
}
function Cursor(str, pos) {
this.str = str
this.pos = pos
this.MoveRight = (step = 1) => {
this.pos += step
}
this.MoveToNextPara = () => {
const curStr = this.str.substring(this.pos)
const match = /^(?<all> *--?(?<name>[a-zA-Z_][a-zA-Z0-9_]*)(=(?<value>[^-]*))?)/g.exec(curStr) // https://regex101.com/r/k004Gv/2
if (match) {
let {groups: {all, name, value}} = match
if (value !== undefined) {
value = value.trim()
if (value.slice(0, 1) === '"') { // string
if (value.slice(-1) !== '"') {
throw new ParserError(`Parsing error: '"' expected`)
}
value = value.slice(1, -1)
} else { // number or string (without '"')
value = isNaN(Number(value)) ? String(value) : Number(value)
}
}
this.MoveRight(all.length)
return [name, value ?? true] // If the value is undefined, then set it as ture.
}
throw new ParserError(`illegal format detected. ${curStr}`)
}
}
function ArgumentParser(str) {
const obj = {}
const cursor = new Cursor(str, 0)
while (1) {
const [name, value] = cursor.MoveToNextPara()
obj[name] = value
if (cursor.pos === str.length) {
return obj
}
}
}
NodeJS exposes a global variable called process.
we can use:
process.argv
to get the command line arguments passes to our script.
The output of process.argv will be a list in the following order:
[
full-path-to-node-executable,
full-path-to-the-script-file
...additonal-arguments-we-provide
]
process.argv is your friend, capturing command line args is natively supported in Node JS. See example below::
process.argv.forEach((val, index) => {
console.log(`${index}: ${val}`);
})
Related
(NodeJS, large JSON file) | stream-json npm package error: Javascript heap out of memory
stream-json noob here. I'm wondering why the below code is running out of memory. context I have a large JSON file. The JSON file's structure is something like this: [ {'id': 1, 'avg_rating': 2}, {'id': 1, 'name': 'Apple'} ] I want to modify it to be [ {'id': 1, 'avg_rating': 2, 'name': 'Apple'} ] In other words, I want to run a reducer function on each element of the values array of the JSON (Object.values(data)) to check if the same id is entered into different keys in the json, and if so "merge" that into one key. The code I wrote to do this is: var chunk = [{'id': 1, 'name': 'a'},{'id': 1, 'avg_rating': 2}] const result = Object.values(chunk.reduce((j, c) => { if (j[c.id]) { j[c.id]['avg_rating'] = c.avg_rating } else { j[c.id] = { ...c }; } return j; }, {})); console.log(result) The thing is, you cannot try to run this on a large JSON file without running out of memory. So, I need to use JSON streaming. the streaming code Looking at the stream-json documentation, I think I need to use a Parser to take in text as a Readable stream of objects and output a stream of data items as Writeable buffer/text "things". The code I can write to do that is: const {chain} = require('stream-chain'); const {parser} = require('stream-json/Parser'); const {streamValues} = require('stream-json/streamers/StreamValues'); const fs = require('fs'); const pipeline = chain([ fs.createReadStream('test.json'), parser(), streamValues(), data => { var chunk = data.value const result = Object.values(chunk.reduce((j, c) => { if (j[c.id]) { j[c.id]['avg_rating'] = c.avg_rating } else { j[c.id] = { ...c }; } return j; }, {})); //console.log(result) return JSON.stringify(result); }, fs.createWriteStream(fpath) ]) To create a write stream (since I do want an output json file), I just added to the parse function above fs.createWriteStream(filepath) , but it looks like -- while this works on a small sample -- this doesn't work for a large JSON file: I get the error "heap out of memory". attempts to fix I think the main issue of the code is that "chunk" philosophy is wrong. If this works via "streaming" a JSON line by line (?), then "chunk" might be trying to save all the data that the program has run into so far, whereas I really only want it to run a reducer function in batches. I then am kind of back at square one .. how would I merge the key-value pairs of a JSON if the id is the same? If the data custom code isn't the problem, then I get the feeling I need to use a Stringer , since I want to edit a stream with custom code, and save it back to a file. However, I can't seem to get how Stringer reads data, as the below code runs an error where data is undefined: const pipeline = chain([ fs.createReadStream('testjson'), parser(), data => { var chunk = data.value const result = Object.values(chunk.reduce((j, c) => { if (j[c.id]) { j[c.id]['avg_rating'] = c.avg_rating } else { j[c.id] = { ...c }; } return j; }, {})); console.log(result) return JSON.stringify(result); }, stringer(), zlib.Gzip(), fs.createWriteStream('edited.json.gz') ]) I would greatly appreciate any advice on this situation or any help diagnosing the problems in my approach. Thank you!!
While this is certainly an interesting question - I have the liberty to just restructure how the data's scraped, and as such can bypass having to do this at all. Thanks all!
JavaScript string is empty when passed to Rust WebAssembly module
When passing a string to a Rust WASM module, the passed data shows up as blank, as per the pattern matching in the real_code::compute function The following code is what I've tried. I don't know if it has to do with how its being returned, but when I pass a hardcoded &str, it works fine. However, the JsInteropString shows as blank. Here is how I encoded the string before sending it to WASM (from Passing a JavaScript string to a Rust function compiled to WebAssembly) const memory = new WebAssembly.Memory({ initial: 20, maximum: 100 }); const importObject = { env: { memory } }; const memoryManager = (memory) => { var base = 0; // NULL is conventionally at address 0, so we "use up" the first 4 // bytes of address space to make our lives a bit simpler. base += 4; return { encodeString: (jsString) => { // Convert the JS String to UTF-8 data const encoder = new TextEncoder(); const encodedString = encoder.encode(jsString); // Organize memory with space for the JsInteropString at the // beginning, followed by the UTF-8 string bytes. const asU32 = new Uint32Array(memory.buffer, base, 2); const asBytes = new Uint8Array(memory.buffer, asU32.byteOffset + asU32.byteLength, encodedString.length); // Copy the UTF-8 into the WASM memory. asBytes.set(encodedString); // Assign the data pointer and length values. asU32[0] = asBytes.byteOffset; asU32[1] = asBytes.length; // Update our memory allocator base address for the next call const originalBase = base; base += asBytes.byteOffset + asBytes.byteLength; return originalBase; } }; }; invoking wasm like this: //...loading and compiling WASM, getting instance from promise (standard) const testStr = "TEST" const input = myMemory.encodeString(testStr); const offset = instance.exports.func_to_call(input); // A struct with a known memory layout that we can pass string information in #[repr(C)] pub struct JsInteropString { data: *const u8, len: usize, } // Our FFI shim function #[no_mangle] pub unsafe extern "C" fn func_to_call(s: *const JsInteropString) -> *mut c_char { // ... check for nulls etc /* THROWS ERROR error[E0609]: no field `data` on type `*const JsInteropString` */ //////let data = std::slice::from_raw_parts(s.data, s.len); //this fixes the above error let data = std::slice::from_raw_parts((*s).data, (*s).len); let dataToPrint: Result<_, _> = std::str::from_utf8(data); let real_res: &str = match dataToPrint { Ok(s) => real_code::compute(dataToPrint.unwrap()), //IS BLANK //Ok(s) => real_code::compute("SUCCESS"), //RETURNS "SUCCESS made it" Err(_) => real_code::compute("ERROR"), }; unsafe { let s = CString::new(real_res).unwrap(); println!("result: {:?}", &s); s.into_raw() } } mod real_code { pub fn compute(operator: &str) -> &str { match operator { "SUCCESS" => "SUCCESS made it", "ERROR" => "ERROR made it", "TEST" => "TEST made it", _ => operator, } } } When the function is called from JavaScript, it should return the same string passed. I even went as far as to update my Rust environment with rustup... any ideas? UPDATE Using a more representative version of the referenced post: #[no_mangle] pub unsafe extern "C" fn compute(s: *const JsInteropString) -> i32 { let s = match s.as_ref() { Some(s) => s, None => return -1, }; // Convert the pointer and length to a `&[u8]`. let data = std::slice::from_raw_parts(s.data, s.len); // Convert the `&[u8]` to a `&str` match std::str::from_utf8(data) { Ok(s) => real_code::compute(s), Err(_) => -2, } } mod real_code { pub fn compute(operator: &str) -> i32 { match operator { "SUCCESS" => 1, "ERROR" => 2, "TEST" => 3, _ => 10, } } } regardless on the string passed through the js encoding, it still returns the default value from compute. I don't know if the referenced post actually solves the problem. So yes, the code compiles. It is a runtime issue I am trying to solve. Something is off about how the string is being encoded and passed to WASM. Update 2 Tried switching my toolchain to Nightly, and what I also found is that the following macros/attributes throw an error regarding unstable attributes #![feature(wasm_import_memory)] #![wasm_import_memory] After realizing that I need to tell rust that memory will be imported, this seems to have fixed the problem of the string being passed as empty. It wasn't empty, it wasnt even passed it seems like. Inside the .cargo file [target.wasm32-unknown-unknown] rustflags = [ "-Clink-args=-s EXPORTED_FUNCTIONS=['_func_to_call'] -s ASSERTIONS=1", "-C", "link-args=--import-memory", ] This seems to may have done the trick! #Shepmaster, update your answer from last year for others who stumble across it, as it has good SEO. This is due to changes in the rust environment.
Why JSON.stringify($('#calendar').fullcalendar('clientEvents')) fails?
As title, when I try to do: myString = JSON.stringify($('#calendar').fullcalendar('clientEvents')); it fails. I tried to alert myString but I see a series of [Object object], ... . But if i try to alert myArray[0].title for example, it returns correctly. Where I'm doing wrong? P.S. The goal is to obtain a string to save on a file via AJAX.
Your results tell you that the objects in the array that the fullCalendar clientEvents method gives you can't be directly converted to JSON. I get slightly different results on the http://fullcalendar.io page (I get an error about trying to convert a circular structure); I assume that's down to differences either in the FullCalendar version you're using vs. they're using, or differences in how your browser and mine deal with circular structures. Either way, the objects apparently can't be used as-is. The goal is to obtain a string to save on a file via AJAX. You can do that by using map on the array to get objects that can be converted to JSON successfully, whitelisting the properties you want (or blacklisting the ones you don't want). Here's an example whitelisting the start, end, and title properties: var json = JSON.stringify($("#calendar").fullCalendar("clientEvents").map(function(e) { return { start: e.start, end: e.end, title: e.title }; })); Heres one blacklisting source and any property starting with _: var json = JSON.stringify($("#calendar").fullCalendar("clientEvents").map(function(e) { var rv = {}; Object.keys(e) .filter(function(k) { return k != "source" && !k.startsWith("_"); }) .forEach(function(k) { rv[k] = e[k]; }); return rv; })); ...which worked for me on their site. Here are ES2015 versions of both of those: Whitelisting: let json = JSON.stringify($("#calendar").fullCalendar("clientEvents").map(e => ({ start: e.start, end: e.end, title: e.title }))); Blacklisting: let json = JSON.stringify($("#calendar").fullCalendar("clientEvents").map(e => { let rv = {}; Object.keys(e) .filter(k => k != "source" && !k.startsWith("_")) .forEach(k => { rv[k] = e[k]; }); return rv; }));
Write objects into file with Node.js
I've searched all over stackoverflow / google for this, but can't seem to figure it out. I'm scraping social media links of a given URL page, and the function returns an object with a list of URLs. When I try to write this data into a different file, it outputs to the file as [object Object] instead of the expected: [ 'https://twitter.com/#!/101Cookbooks', 'http://www.facebook.com/101cookbooks'] as it does when I console.log() the results. This is my sad attempt to read and write a file in Node, trying to read each line(the url) and input through a function call request(line, gotHTML): fs.readFileSync('./urls.txt').toString().split('\n').forEach(function (line){ console.log(line); var obj = request(line, gotHTML); console.log(obj); fs.writeFileSync('./data.json', obj , 'utf-8'); }); for reference -- the gotHTML function: function gotHTML(err, resp, html){ var social_ids = []; if(err){ return console.log(err); } else if (resp.statusCode === 200){ var parsedHTML = $.load(html); parsedHTML('a').map(function(i, link){ var href = $(link).attr('href'); for(var i=0; i<socialurls.length; i++){ if(socialurls[i].test(href) && social_ids.indexOf(href) < 0 ) { social_ids.push(href); }; }; }) }; return social_ids; };
Building on what deb2fast said I would also pass in a couple of extra parameters to JSON.stringify() to get it to pretty format: fs.writeFileSync('./data.json', JSON.stringify(obj, null, 2) , 'utf-8'); The second param is an optional replacer function which you don't need in this case so null works. The third param is the number of spaces to use for indentation. 2 and 4 seem to be popular choices.
obj is an array in your example. fs.writeFileSync(filename, data, [options]) requires either String or Buffer in the data parameter. see docs. Try to write the array in a string format: // writes 'https://twitter.com/#!/101Cookbooks', 'http://www.facebook.com/101cookbooks' fs.writeFileSync('./data.json', obj.join(',') , 'utf-8'); Or: // writes ['https://twitter.com/#!/101Cookbooks', 'http://www.facebook.com/101cookbooks'] var util = require('util'); fs.writeFileSync('./data.json', util.inspect(obj) , 'utf-8'); edit: The reason you see the array in your example is because node's implementation of console.log doesn't just call toString, it calls util.format see console.js source
If you're geting [object object] then use JSON.stringify fs.writeFile('./data.json', JSON.stringify(obj) , 'utf-8'); It worked for me.
In my experience JSON.stringify is slightly faster than util.inspect. I had to save the result object of a DB2 query as a json file, The query returned an object of 92k rows, the conversion took very long to complete with util.inspect, so I did the following test by writing the same 1000 record object to a file with both methods. JSON.Stringify fs.writeFile('./data.json', JSON.stringify(obj, null, 2)); Time: 3:57 (3 min 57 sec) Result's format: [ { "PROB": "00001", "BO": "AXZ", "CNTRY": "649" }, ... ] util.inspect var util = require('util'); fs.writeFile('./data.json', util.inspect(obj, false, 2, false)); Time: 4:12 (4 min 12 sec) Result's format: [ { PROB: '00001', BO: 'AXZ', CNTRY: '649' }, ... ]
Could you try doing JSON.stringify(obj); Like this: var stringify = JSON.stringify(obj); fs.writeFileSync('./data.json', stringify, 'utf-8');
Just incase anyone else stumbles across this, I use the fs-extra library in node and write javascript objects to a file like this: const fse = require('fs-extra'); fse.outputJsonSync('path/to/output/file.json', objectToWriteToFile);
Further to #Jim Schubert's and #deb2fast's answers: To be able to write out large objects of order which are than ~100 MB, you'll need to use for...of as shown below and match to your requirements. const fsPromises = require('fs').promises; const sampleData = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}; const writeToFile = async () => { for (const dataObject of Object.keys(sampleData)) { console.log(sampleData[dataObject]); await fsPromises.appendFile( "out.json" , dataObject +": "+ JSON.stringify(sampleData[dataObject])); } } writeToFile(); Refer https://stackoverflow.com/a/67699911/3152654 for full reference for node.js limits
How can I guarantee that my enums definition doesn't change in JavaScript?
Would the following make the objects fulfil all characteristics that enums have in JavaScript? Something like: my.namespace.ColorEnum = { RED : 0, GREEN : 1, BLUE : 2 } // later on if(currentColor == my.namespace.ColorEnum.RED) { // whatever } Or is there some other way I can do this?
Since 1.8.5 it's possible to seal and freeze the object, so define the above as: const DaysEnum = Object.freeze({"monday":1, "tuesday":2, "wednesday":3, ...}) or const DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...} Object.freeze(DaysEnum) and voila! JS enums. However, this doesn't prevent you from assigning an undesired value to a variable, which is often the main goal of enums: let day = DaysEnum.tuesday day = 298832342 // goes through without any errors One way to ensure a stronger degree of type safety (with enums or otherwise) is to use a tool like TypeScript or Flow. Quotes aren't needed but I kept them for consistency.
This isn't much of an answer, but I'd say that works just fine, personally Having said that, since it doesn't matter what the values are (you've used 0, 1, 2), I'd use a meaningful string in case you ever wanted to output the current value.
UPDATE I don't think my answer below is the best way to write enums in JavaScript anymore. See my blog post for more details: Enums in JavaScript. Alerting the name is already possible: if (currentColor == my.namespace.ColorEnum.RED) { // alert name of currentColor (RED: 0) var col = my.namespace.ColorEnum; for (var name in col) { if (col[name] == col.RED) alert(name); } } Alternatively, you could make the values objects, so you can have the cake and eat it too: var SIZE = { SMALL : {value: 0, name: "Small", code: "S"}, MEDIUM: {value: 1, name: "Medium", code: "M"}, LARGE : {value: 2, name: "Large", code: "L"} }; var currentSize = SIZE.MEDIUM; if (currentSize == SIZE.MEDIUM) { // this alerts: "1: Medium" alert(currentSize.value + ": " + currentSize.name); } In JavaScript, as it is a dynamic language, it is even possible to add enum values to the set later: // Add EXTRALARGE size SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"}; Remember, the fields of the enum (value, name and code in this example) are not needed for the identity check and are only there for convenience. Also the name of the size property itself does not need to be hard coded, but can also be set dynamically. So supposing you only know the name for your new enum value, you can still add it without problems: // Add 'Extra Large' size, only knowing it's name var name = "Extra Large"; SIZE[name] = {value: -1, name: name, code: "?"}; Of course this means that some assumptions can no longer be made (that value represents the correct order for the size for example). Remember, in JavaScript an object is just like a map or hash table. A set of name-value pairs. You can loop through them or otherwise manipulate them without knowing much about them in advance. Example for (var sz in SIZE) { // sz will be the names of the objects in SIZE, so // 'SMALL', 'MEDIUM', 'LARGE', 'EXTRALARGE' var size = SIZE[sz]; // Get the object mapped to the name in sz for (var prop in size) { // Get all the properties of the size object, iterates over // 'value', 'name' and 'code'. You can inspect everything this way. } } And by the way, if you are interested in namespaces, you may want to have a look at my solution for simple but powerful namespace and dependency management for JavaScript: Packages JS
Bottom line: You can't. You can fake it, but you won't get type safety. Typically this is done by creating a simple dictionary of string values mapped to integer values. For example: var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...} Document.Write("Enumerant: " + DaysEnum.tuesday); The problem with this approach? You can accidentally redefine your enumerant, or accidentally have duplicate enumerant values. For example: DaysEnum.monday = 4; // whoops, monday is now thursday, too Edit What about Artur Czajka's Object.freeze? Wouldn't that work to prevent you from setting monday to thursday? โ Fry Quad Absolutely, Object.freeze would totally fix the problem I complained about. I would like to remind everyone that when I wrote the above, Object.freeze didn't really exist. Now.... now it opens up some very interesting possibilities. Edit 2 Here's a very good library for creating enums. http://www.2ality.com/2011/10/enums.html While it probably doesn't fit every valid use of enums, it goes a very long way.
Here's what we all want: function Enum(constantsList) { for (var i in constantsList) { this[constantsList[i]] = i; } } Now you can create your enums: var YesNo = new Enum(['NO', 'YES']); var Color = new Enum(['RED', 'GREEN', 'BLUE']); By doing this, constants can be acessed in the usual way (YesNo.YES, Color.GREEN) and they get a sequential int value (NO = 0, YES = 1; RED = 0, GREEN = 1, BLUE = 2). You can also add methods, by using Enum.prototype: Enum.prototype.values = function() { return this.allValues; /* for the above to work, you'd need to do this.allValues = constantsList at the constructor */ }; Edit - small improvement - now with varargs: (unfortunately it doesn't work properly on IE :S... should stick with previous version then) function Enum() { for (var i in arguments) { this[arguments[i]] = i; } } var YesNo = new Enum('NO', 'YES'); var Color = new Enum('RED', 'GREEN', 'BLUE');
In most modern browsers, there is a symbol primitive data type which can be used to create an enumeration. It will ensure type safety of the enum as each symbol value is guaranteed by JavaScript to be unique, i.e. Symbol() != Symbol(). For example: const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()}); To simplify debugging, you can add a description to enum values: const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")}); Plunker demo On GitHub you can find a wrapper that simplifies the code required to initialize the enum: const color = new Enum("RED", "BLUE") color.RED.toString() // Symbol(RED) color.getName(color.RED) // RED color.size // 2 color.values() // Symbol(RED), Symbol(BLUE) color.toString() // RED,BLUE
๐ฆ๐ฒ๐น๐ณ-๐๐ฒ๐๐ฐ๐ฟ๐ถ๐ฝ๐๐ถ๐๐ฒ ๐๐ ๐๐ฒ๐ป๐๐ถ๐ฏ๐น๐ฒ ๐ฉ๐ฎ๐ฟ๐ถ๐ฎ๐ฏ๐น๐ฒ ๐ก๐ฎ๐บ๐ฒ๐ Let's cut straight to the problem: file size. Every other answer listed here bloats your minified code to the extreme. I present to you that for the best possible reduction in code size by minification, performance, readability of code, large scale project management, and syntax hinting in many code editors, this is the correct way to do enumerations: underscore-notation variables. As demonstrated in the chart above and example below, here are five easy steps to get started: Determine a name for the enumeration group. Think of a noun that can describe the purpose of the enumeration or at least the entries in the enumeration. For example, a group of enumerations representing colors choosable by the user might be better named COLORCHOICES than COLORS. Decide whether enumerations in the group are mutually-exclusive or independent. If mutually-exclusive, start each enumerated variable name with ENUM_. If independent or side-by-side, use INDEX_. For each entry, create a new local variable whose name starts with ENUM_ or INDEX_, then the name of the group, then an underscore, then a unique friendly name for the property Add a ENUMLENGTH_, ENUMLEN_, INDEXLENGTH_, or INDEXLEN_ (whether LEN_ or LENGTH_ is personal preference) enumerated variable at the very end. You should use this variable wherever possible in your code to ensure that adding an extra entry to the enumeration and incrementing this value won't break your code. Give each successive enumerated variable a value one more than the last, starting at 0. There are comments on this page that say 0 should not be used as an enumerated value because 0 == null, 0 == false, 0 == "", and other JS craziness. I submit to you that, to avoid this problem and boost performance at the same time, always use === and never let == appear in your code except with typeof (e.x. typeof X == "string"). In all my years of using ===, I have never once had a problem with using 0 as an enumeration value. If you are still squeamish, then 1 could be used as the starting value in ENUM_ enumerations (but not in INDEX_ enumerations) without performance penalty in many cases. const ENUM_COLORENUM_RED = 0; const ENUM_COLORENUM_GREEN = 1; const ENUM_COLORENUM_BLUE = 2; const ENUMLEN_COLORENUM = 3; // later on if(currentColor === ENUM_COLORENUM_RED) { // whatever } Here is how I remember when to use INDEX_ and when to use ENUM_: // Precondition: var arr = []; // arr[INDEX_] = ENUM_; However, ENUM_ can, in certain circumstances, be appropriate as an index such as when counting the occurrences of each item. const ENUM_PET_CAT = 0, ENUM_PET_DOG = 1, ENUM_PET_RAT = 2, ENUMLEN_PET = 3; var favoritePets = [ENUM_PET_CAT, ENUM_PET_DOG, ENUM_PET_RAT, ENUM_PET_DOG, ENUM_PET_DOG, ENUM_PET_CAT, ENUM_PET_RAT, ENUM_PET_CAT, ENUM_PET_DOG]; var petsFrequency = []; for (var i=0; i<ENUMLEN_PET; i=i+1|0) petsFrequency[i] = 0; for (var i=0, len=favoritePets.length|0, petId=0; i<len; i=i+1|0) petsFrequency[petId = favoritePets[i]|0] = (petsFrequency[petId]|0) + 1|0; console.log({ "cat": petsFrequency[ENUM_PET_CAT], "dog": petsFrequency[ENUM_PET_DOG], "rat": petsFrequency[ENUM_PET_RAT] }); Observe that, in the code above, it's really easy to add in a new kind of pet: you would just have to append a new entry after ENUM_PET_RAT and update ENUMLEN_PET accordingly. It might be more difficult and buggy to add a new entry in other systems of enumeration. ๐๐ ๐๐ฒ๐ป๐ฑ ๐จ๐ฝ๐ฝ๐ฒ๐ฟ๐ฐ๐ฎ๐๐ฒ ๐ฉ๐ฎ๐ฟ๐ถ๐ฎ๐ฏ๐น๐ฒ๐ ๐ช๐ถ๐๐ต ๐๐ฑ๐ฑ๐ถ๐๐ถ๐ผ๐ป Additionally, this syntax of enumerations allows for clear and concise class extending as seen below. To extend a class, add an incrementing number to the LEN_ entry of the parent class. Then, finish out the subclass with its own LEN_ entry so that the subclass may be extended further in the future. (function(window){ "use strict"; var parseInt = window.parseInt; // use INDEX_ when representing the index in an array instance const INDEX_PIXELCOLOR_TYPE = 0, // is a ENUM_PIXELTYPE INDEXLEN_PIXELCOLOR = 1, INDEX_SOLIDCOLOR_R = INDEXLEN_PIXELCOLOR+0, INDEX_SOLIDCOLOR_G = INDEXLEN_PIXELCOLOR+1, INDEX_SOLIDCOLOR_B = INDEXLEN_PIXELCOLOR+2, INDEXLEN_SOLIDCOLOR = INDEXLEN_PIXELCOLOR+3, INDEX_ALPHACOLOR_R = INDEXLEN_PIXELCOLOR+0, INDEX_ALPHACOLOR_G = INDEXLEN_PIXELCOLOR+1, INDEX_ALPHACOLOR_B = INDEXLEN_PIXELCOLOR+2, INDEX_ALPHACOLOR_A = INDEXLEN_PIXELCOLOR+3, INDEXLEN_ALPHACOLOR = INDEXLEN_PIXELCOLOR+4, // use ENUM_ when representing a mutually-exclusive species or type ENUM_PIXELTYPE_SOLID = 0, ENUM_PIXELTYPE_ALPHA = 1, ENUM_PIXELTYPE_UNKNOWN = 2, ENUMLEN_PIXELTYPE = 2; function parseHexColor(inputString) { var rawstr = inputString.trim().substring(1); var result = []; if (rawstr.length === 8) { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA; result[INDEX_ALPHACOLOR_R] = parseInt(rawstr.substring(0,2), 16); result[INDEX_ALPHACOLOR_G] = parseInt(rawstr.substring(2,4), 16); result[INDEX_ALPHACOLOR_B] = parseInt(rawstr.substring(4,6), 16); result[INDEX_ALPHACOLOR_A] = parseInt(rawstr.substring(4,6), 16); } else if (rawstr.length === 4) { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_ALPHA; result[INDEX_ALPHACOLOR_R] = parseInt(rawstr[0], 16) * 0x11; result[INDEX_ALPHACOLOR_G] = parseInt(rawstr[1], 16) * 0x11; result[INDEX_ALPHACOLOR_B] = parseInt(rawstr[2], 16) * 0x11; result[INDEX_ALPHACOLOR_A] = parseInt(rawstr[3], 16) * 0x11; } else if (rawstr.length === 6) { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID; result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr.substring(0,2), 16); result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr.substring(2,4), 16); result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr.substring(4,6), 16); } else if (rawstr.length === 3) { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_SOLID; result[INDEX_SOLIDCOLOR_R] = parseInt(rawstr[0], 16) * 0x11; result[INDEX_SOLIDCOLOR_G] = parseInt(rawstr[1], 16) * 0x11; result[INDEX_SOLIDCOLOR_B] = parseInt(rawstr[2], 16) * 0x11; } else { result[INDEX_PIXELCOLOR_TYPE] = ENUM_PIXELTYPE_UNKNOWN; } return result; } // the red component of green console.log(parseHexColor("#0f0")[INDEX_SOLIDCOLOR_R]); // the alpha of transparent purple console.log(parseHexColor("#f0f7")[INDEX_ALPHACOLOR_A]); // the enumerated array for turquoise console.log(parseHexColor("#40E0D0")); })(self); (Length: 2,450 bytes) Some may say that this is less practical than other solutions: it wastes tons of space, it takes a long time to write, and it is not coated with sugar syntax. Those people would be right if they do not minify their code. However, no reasonable person would leave unminified code in the end product. For this minification, Closure Compiler is the best I have yet to find. Online access can be found here. Closure compiler is able to take all of this enumeration data and inline it, making your Javascript be super duper small and run super duper fast. Thus, Minify with Closure Compiler. Observe. ๐ ๐ถ๐ป๐ถ๐ณ๐ ๐ช๐ถ๐๐ต ๐๐น๐ผ๐๐๐ฟ๐ฒ ๐๐ผ๐บ๐ฝ๐ถ๐น๐ฒ๐ฟ Closure compiler is able to perform some pretty incredible optimizations via inferences that are way beyond the capacities of any other Javascript minifier. Closure Compiler is able to inline primitive variables set to a fixed value. Closure Compiler is also able to make inferences based upon these inlined values and eliminate unused blocks in if-statements and loops. 'use strict';(function(e){function d(a){a=a.trim().substring(1);var b=[];8===a.length?(b[0]=1,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16),b[4]=c(a.substring(4,6),16)):4===a.length?(b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16),b[4]=17*c(a[3],16)):6===a.length?(b[0]=0,b[1]=c(a.substring(0,2),16),b[2]=c(a.substring(2,4),16),b[3]=c(a.substring(4,6),16)):3===a.length?(b[0]=0,b[1]=17*c(a[0],16),b[2]=17*c(a[1],16),b[3]=17*c(a[2],16)):b[0]=2;return b}var c= e.parseInt;console.log(d("#0f0")[1]);console.log(d("#f0f7")[4]);console.log(d("#40E0D0"))})(self); (Length: 605 bytes) Closure Compiler rewards you for coding smarter and organizing your code well because, whereas many minifiers punish organized code with a bigger minified file size, Closure Compiler is able to sift through all your cleanliness and sanity to output an even smaller file size if you use tricks like variable name enumerations. That, in this one mind, is the holy grail of coding: a tool that both assists your code with a smaller minified size and assists your mind by training better programming habits. ๐ฆ๐บ๐ฎ๐น๐น๐ฒ๐ฟ ๐๐ผ๐ฑ๐ฒ ๐ฆ๐ถ๐๐ฒ Now, let us see how big the equivalent file would be without any of these enumerations. Source Without Using Enumerations (length: 1,973 bytes (477 bytes shorter than enumerated code!)) Minified Without Using Enumerations (length: 843 bytes (238 bytes longer than enumerated code)) As seen, without enumerations, the source code is shorter at the cost of a larger minified code. I do not know about you; but I know for sure that I do not incorporate source code into the end product. Thus, this form of enumerations is far superior insomuch that it results in smaller minified file sizes. ๐๐ผ๐ผ๐ฝ๐ฒ๐ฟ๐ฎ๐๐ถ๐๐ฒ ๐ค ๐๐๐ด ๐๐ถ๐ ๐ถ๐ป๐ด Another advantage about this form of enumeration is that it can be used to easily manage large scale projects without sacrificing minified code size. When working on a large project with lots of other people, it might be beneficial to explicitly mark and label the variable names with who created the code so that the original creator of the code can be quickly identified for collaborative bug fixing. // JG = Jack Giffin const ENUM_JG_COLORENUM_RED = 0, ENUM_JG_COLORENUM_GREEN = 1, ENUM_JG_COLORENUM_BLUE = 2, ENUMLEN_JG_COLORENUM = 3; // later on if(currentColor === ENUM_JG_COLORENUM_RED) { // whatever } // PL = Pepper Loftus // BK = Bob Knight const ENUM_PL_ARRAYTYPE_UNSORTED = 0, ENUM_PL_ARRAYTYPE_ISSORTED = 1, ENUM_BK_ARRAYTYPE_CHUNKED = 2, // added by Bob Knight ENUM_JG_ARRAYTYPE_INCOMPLETE = 3, // added by jack giffin ENUMLEN_PL_COLORENUM = 4; // later on if( randomArray === ENUM_PL_ARRAYTYPE_UNSORTED || randomArray === ENUM_BK_ARRAYTYPE_CHUNKED ) { // whatever } ๐ฆ๐๐ฝ๐ฒ๐ฟ๐ถ๐ผ๐ฟ ๐ฃ๐ฒ๐ฟ๐ณ๐ผ๐ฟ๐บ๐ฎ๐ป๐ฐ๐ฒ Further, this form of enumeration is also much faster after minification. In normal named properties, the browser has to use hashmaps to look up where the property is on the object. Although JIT compilers intelligently cache this location on the object, there is still tremendous overhead due to special cases such as deleting a lower property from the object. But, with continuous non-sparse integer-indexed PACKED_ELEMENTS arrays, the browser is able to skip much of that overhead because the index of the value in the internal array is already specified. Yes, according to the ECMAScript standard, all properties are supposed to be treated as strings. Nevertheless, this aspect of the ECMAScript standard is very misleading about performance because all browsers have special optimizations for numeric indexes in arrays. /// Hashmaps are slow, even with JIT juice var ref = {}; ref.count = 10; ref.value = "foobar"; Compare the code above to the code below. /// Arrays, however, are always lightning fast const INDEX_REFERENCE_COUNT = 0; const INDEX_REFERENCE_VALUE = 1; const INDEXLENGTH_REFERENCE = 2; var ref = []; ref[INDEX_REFERENCE_COUNT] = 10; ref[INDEX_REFERENCE_VALUE] = "foobar"; One might object to the code with enumerations seeming to be much longer than the code with ordinary objects, but looks can be deceiving. It is important to remember that source code size is not proportional to output size when using the epic Closure Compiler. Observe. /// Hashmaps are slow, even with JIT juice var a={count:10,value:"foobar"}; The minified code without enumerations is above and the minified code with enumerations is below. /// Arrays, however, are always lightning fast var a=[10,"foobar"]; The example above demonstrates that, in addition to having superior performance, the enumerated code also results in a smaller minified file size. ๐๐ฎ๐๐ ๐๐ฒ๐ฏ๐๐ด๐ด๐ถ๐ป๐ด Furthermore, this one's personal cherry on the top is using this form of enumerations along with the CodeMirror text editor in Javascript mode. CodeMirror's Javascript syntax highlighting mode highlights local variables in the current scope. That way, you know instantly when you type in a variable name correctly because if the variable name was previously declared with the var keyword, then the variable name turns a special color (cyan by default). Even if you do not use CodeMirror, then at least the browser throws a helpful [variable name] is not defined exception when executing code with mistyped enumeration names. Also, JavaScript tools such as JSLint and Closure Compiler are very loud about telling you when you mistype in an enumeration variable name. CodeMirror, the browser, and various Javascript tools put together make debugging this form of enumeration very simple and really easy. const ENUM_COLORENUM_RED = 0, ENUM_COLORENUM_GREEN = 1, ENUM_COLORENUM_BLUE = 2, ENUMLEN_COLORENUM = 3; var currentColor = ENUM_COLORENUM_GREEN; if(currentColor === ENUM_COLORENUM_RED) { // whatever } if(currentColor === ENUM_COLORENUM_DNE) { // whatever } In the above snippet, you were alerted with an error because ENUM_COLORENUM_DNE does not exist. ๐๐ผ๐ป๐ฐ๐น๐๐๐ถ๐ผ๐ป I think its safe to say that this methodology of enumeration is indeed the best way to go not just for minified code size, but also for performance, debugging, and collaboration.
Use Javascript Proxies TLDR: Add this class to your utility methods and use it throughout your code, it mocks Enum behavior from traditional programming languages, and actually throws errors when you try to either access an enumerator that does not exist or add/update an enumerator. No need to rely on Object.freeze(). class Enum { constructor(enumObj) { const handler = { get(target, name) { if (typeof target[name] != 'undefined') { return target[name]; } throw new Error(`No such enumerator: ${name}`); }, set() { throw new Error('Cannot add/update properties on an Enum instance after it is defined') } }; return new Proxy(enumObj, handler); } } Then create enums by instantiating the class: const roles = new Enum({ ADMIN: 'Admin', USER: 'User', }); Full Explanation: One very beneficial feature of Enums that you get from traditional languages is that they blow up (throw a compile-time error) if you try to access an enumerator which does not exist. Besides freezing the mocked enum structure to prevent additional values from accidentally/maliciously being added, none of the other answers address that intrinsic feature of Enums. As you are probably aware, accessing non-existing members in JavaScript simply returns undefined and does not blow up your code. Since enumerators are predefined constants (i.e. days of the week), there should never be a case when an enumerator should be undefined. Don't get me wrong, JavaScript's behavior of returning undefined when accessing undefined properties is actually a very powerful feature of language, but it's not a feature you want when you are trying to mock traditional Enum structures. This is where Proxy objects shine. Proxies were standardized in the language with the introduction of ES6 (ES2015). Here's the description from MDN: The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc). Similar to a web server proxy, JavaScript proxies are able to intercept operations on objects (with the use of "traps", call them hooks if you like) and allow you to perform various checks, actions and/or manipulations before they complete (or in some cases stopping the operations altogether which is exactly what we want to do if and when we try to reference an enumerator which does not exist). Here's a contrived example that uses the Proxy object to mimic Enums. The enumerators in this example are standard HTTP Methods (i.e. "GET", "POST", etc.): // Class for creating enums (13 lines) // Feel free to add this to your utility library in // your codebase and profit! Note: As Proxies are an ES6 // feature, some browsers/clients may not support it and // you may need to transpile using a service like babel class Enum { // The Enum class instantiates a JavaScript Proxy object. // Instantiating a `Proxy` object requires two parameters, // a `target` object and a `handler`. We first define the handler, // then use the handler to instantiate a Proxy. // A proxy handler is simply an object whose properties // are functions which define the behavior of the proxy // when an operation is performed on it. // For enums, we need to define behavior that lets us check what enumerator // is being accessed and what enumerator is being set. This can be done by // defining "get" and "set" traps. constructor(enumObj) { const handler = { get(target, name) { if (typeof target[name] != 'undefined') { return target[name] } throw new Error(`No such enumerator: ${name}`) }, set() { throw new Error('Cannot add/update properties on an Enum instance after it is defined') } } // Freeze the target object to prevent modifications return new Proxy(enumObj, handler) } } // Now that we have a generic way of creating Enums, lets create our first Enum! const httpMethods = new Enum({ DELETE: "DELETE", GET: "GET", OPTIONS: "OPTIONS", PATCH: "PATCH", POST: "POST", PUT: "PUT" }) // Sanity checks console.log(httpMethods.DELETE) // logs "DELETE" try { httpMethods.delete = "delete" } catch (e) { console.log("Error: ", e.message) } // throws "Cannot add/update properties on an Enum instance after it is defined" try { console.log(httpMethods.delete) } catch (e) { console.log("Error: ", e.message) } // throws "No such enumerator: delete" ASIDE: What the heck is a proxy? I remember when I first started seeing the word proxy everywhere, it definitely didn't make sense to me for a long time. If that's you right now, I think an easy way to generalize proxies is to think of them as software, institutions, or even people that act as intermediaries or middlemen between two servers, companies, or people.
I've been playing around with this, as I love my enums. =) Using Object.defineProperty I think I came up with a somewhat viable solution. Here's a jsfiddle: http://jsfiddle.net/ZV4A6/ Using this method.. you should (in theory) be able to call and define enum values for any object, without affecting other attributes of that object. Object.defineProperty(Object.prototype,'Enum', { value: function() { for(i in arguments) { Object.defineProperty(this,arguments[i], { value:parseInt(i), writable:false, enumerable:true, configurable:true }); } return this; }, writable:false, enumerable:false, configurable:false }); Because of the attribute writable:false this should make it type safe. So you should be able to create a custom object, then call Enum() on it. The values assigned start at 0 and increment per item. var EnumColors={}; EnumColors.Enum('RED','BLUE','GREEN','YELLOW'); EnumColors.RED; // == 0 EnumColors.BLUE; // == 1 EnumColors.GREEN; // == 2 EnumColors.YELLOW; // == 3
This is an old one I know, but the way it has since been implemented via the TypeScript interface is: var MyEnum; (function (MyEnum) { MyEnum[MyEnum["Foo"] = 0] = "Foo"; MyEnum[MyEnum["FooBar"] = 2] = "FooBar"; MyEnum[MyEnum["Bar"] = 1] = "Bar"; })(MyEnum|| (MyEnum= {})); This enables you to look up on both MyEnum.Bar which returns 1, and MyEnum[1] which returns "Bar" regardless of the order of declaration.
In ES7 , you can do an elegant ENUM relying on static attributes: class ColorEnum { static RED = 0 ; static GREEN = 1; static BLUE = 2; } then if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/} The advantage ( of using class instead of literal object) is to have a parent class Enum then all your Enums will extends that class. class ColorEnum extends Enum {/*....*/}
Create an object literal: const Modes = { DRAGGING: 'drag', SCALING: 'scale', CLICKED: 'click' };
This is the solution that I use. function Enum() { this._enums = []; this._lookups = {}; } Enum.prototype.getEnums = function() { return _enums; } Enum.prototype.forEach = function(callback){ var length = this._enums.length; for (var i = 0; i < length; ++i){ callback(this._enums[i]); } } Enum.prototype.addEnum = function(e) { this._enums.push(e); } Enum.prototype.getByName = function(name) { return this[name]; } Enum.prototype.getByValue = function(field, value) { var lookup = this._lookups[field]; if(lookup) { return lookup[value]; } else { this._lookups[field] = ( lookup = {}); var k = this._enums.length - 1; for(; k >= 0; --k) { var m = this._enums[k]; var j = m[field]; lookup[j] = m; if(j == value) { return m; } } } return null; } function defineEnum(definition) { var k; var e = new Enum(); for(k in definition) { var j = definition[k]; e[k] = j; e.addEnum(j) } return e; } And you define your enums like this: var COLORS = defineEnum({ RED : { value : 1, string : 'red' }, GREEN : { value : 2, string : 'green' }, BLUE : { value : 3, string : 'blue' } }); And this is how you access your enums: COLORS.BLUE.string COLORS.BLUE.value COLORS.getByName('BLUE').string COLORS.getByValue('value', 1).string COLORS.forEach(function(e){ // do what you want with e }); I usually use the last 2 methods for mapping enums from message objects. Some advantages to this approach: Easy to declare enums Easy to access your enums Your enums can be complex types The Enum class has some associative caching if you are using getByValue a lot Some disadvantages: Some messy memory management going on in there, as I keep the references to the enums Still no type safety
If you're using Backbone, you can get full-blown enum functionality (find by id, name, custom members) for free using Backbone.Collection. // enum instance members, optional var Color = Backbone.Model.extend({ print : function() { console.log("I am " + this.get("name")) } }); // enum creation var Colors = new Backbone.Collection([ { id : 1, name : "Red", rgb : 0xFF0000}, { id : 2, name : "Green" , rgb : 0x00FF00}, { id : 3, name : "Blue" , rgb : 0x0000FF} ], { model : Color }); // Expose members through public fields. Colors.each(function(color) { Colors[color.get("name")] = color; }); // using Colors.Red.print()
your answers are far too complicated var buildSet = function(array) { var set = {}; for (var i in array) { var item = array[i]; set[item] = item; } return set; } var myEnum = buildSet(['RED','GREEN','BLUE']); // myEnum.RED == 'RED' ...etc
I've modified the solution of Andre 'Fi': function Enum() { var that = this; for (var i in arguments) { that[arguments[i]] = i; } this.name = function(value) { for (var key in that) { if (that[key] == value) { return key; } } }; this.exist = function(value) { return (typeof that.name(value) !== "undefined"); }; if (Object.freeze) { Object.freeze(that); } } Test: var Color = new Enum('RED', 'GREEN', 'BLUE'); undefined Color.name(Color.REDs) undefined Color.name(Color.RED) "RED" Color.exist(Color.REDs) false Color.exist(Color.RED) true
I came up with this approach which is modeled after enums in Java. These are type-safe, and so you can perform instanceof checks as well. You can define enums like this: var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]); Days now refers to the Days enum: Days.Monday instanceof Days; // true Days.Friday.name(); // "Friday" Days.Friday.ordinal(); // 4 Days.Sunday === Days.Sunday; // true Days.Sunday === Days.Friday; // false Days.Sunday.toString(); // "Sunday" Days.toString() // "Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } " Days.values().map(function(e) { return e.name(); }); //["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] Days.values()[4].name(); //"Friday" Days.fromName("Thursday") === Days.Thursday // true Days.fromName("Wednesday").name() // "Wednesday" Days.Friday.fromName("Saturday").name() // "Saturday" The implementation: var Enum = (function () { /** * Function to define an enum * #param typeName - The name of the enum. * #param constants - The constants on the enum. Can be an array of strings, or an object where each key is an enum * constant, and the values are objects that describe attributes that can be attached to the associated constant. */ function define(typeName, constants) { /** Check Arguments **/ if (typeof typeName === "undefined") { throw new TypeError("A name is required."); } if (!(constants instanceof Array) && (Object.getPrototypeOf(constants) !== Object.prototype)) { throw new TypeError("The constants parameter must either be an array or an object."); } else if ((constants instanceof Array) && constants.length === 0) { throw new TypeError("Need to provide at least one constant."); } else if ((constants instanceof Array) && !constants.reduce(function (isString, element) { return isString && (typeof element === "string"); }, true)) { throw new TypeError("One or more elements in the constant array is not a string."); } else if (Object.getPrototypeOf(constants) === Object.prototype && !Object.keys(constants).reduce(function (isObject, constant) { return Object.getPrototypeOf(constants[constant]) === Object.prototype; }, true)) { throw new TypeError("One or more constants do not have an associated object-value."); } var isArray = (constants instanceof Array); var isObject = !isArray; /** Private sentinel-object used to guard enum constructor so that no one else can create enum instances **/ function __() { }; /** Dynamically define a function with the same name as the enum we want to define. **/ var __enum = new Function(["__"], "return function " + typeName + "(sentinel, name, ordinal) {" + "if(!(sentinel instanceof __)) {" + "throw new TypeError(\"Cannot instantiate an instance of " + typeName + ".\");" + "}" + "this.__name = name;" + "this.__ordinal = ordinal;" + "}" )(__); /** Private objects used to maintain enum instances for values(), and to look up enum instances for fromName() **/ var __values = []; var __dict = {}; /** Attach values() and fromName() methods to the class itself (kind of like static methods). **/ Object.defineProperty(__enum, "values", { value: function () { return __values; } }); Object.defineProperty(__enum, "fromName", { value: function (name) { var __constant = __dict[name] if (__constant) { return __constant; } else { throw new TypeError(typeName + " does not have a constant with name " + name + "."); } } }); /** * The following methods are available to all instances of the enum. values() and fromName() need to be * available to each constant, and so we will attach them on the prototype. But really, they're just * aliases to their counterparts on the prototype. */ Object.defineProperty(__enum.prototype, "values", { value: __enum.values }); Object.defineProperty(__enum.prototype, "fromName", { value: __enum.fromName }); Object.defineProperty(__enum.prototype, "name", { value: function () { return this.__name; } }); Object.defineProperty(__enum.prototype, "ordinal", { value: function () { return this.__ordinal; } }); Object.defineProperty(__enum.prototype, "valueOf", { value: function () { return this.__name; } }); Object.defineProperty(__enum.prototype, "toString", { value: function () { return this.__name; } }); /** * If constants was an array, we can the element values directly. Otherwise, we will have to use the keys * from the constants object. */ var _constants = constants; if (isObject) { _constants = Object.keys(constants); } /** Iterate over all constants, create an instance of our enum for each one, and attach it to the enum type **/ _constants.forEach(function (name, ordinal) { // Create an instance of the enum var __constant = new __enum(new __(), name, ordinal); // If constants was an object, we want to attach the provided attributes to the instance. if (isObject) { Object.keys(constants[name]).forEach(function (attr) { Object.defineProperty(__constant, attr, { value: constants[name][attr] }); }); } // Freeze the instance so that it cannot be modified. Object.freeze(__constant); // Attach the instance using the provided name to the enum type itself. Object.defineProperty(__enum, name, { value: __constant }); // Update our private objects __values.push(__constant); __dict[name] = __constant; }); /** Define a friendly toString method for the enum **/ var string = typeName + " { " + __enum.values().map(function (c) { return c.name(); }).join(", ") + " } "; Object.defineProperty(__enum, "toString", { value: function () { return string; } }); /** Freeze our private objects **/ Object.freeze(__values); Object.freeze(__dict); /** Freeze the prototype on the enum and the enum itself **/ Object.freeze(__enum.prototype); Object.freeze(__enum); /** Return the enum **/ return __enum; } return { define: define } })();
var ColorEnum = { red: {}, green: {}, blue: {} } You don't need to make sure you don't assign duplicate numbers to different enum values this way. A new object gets instantiated and assigned to all enum values.
IE8 does Not support freeze() method. Source: http://kangax.github.io/compat-table/es5/, Click on "Show obsolete browsers?" on top, and check IE8 & freeze row col intersection. In my current game project, I have used below, since few customers still use IE8: var CONST_WILD_TYPES = { REGULAR: 'REGULAR', EXPANDING: 'EXPANDING', STICKY: 'STICKY', SHIFTING: 'SHIFTING' }; We could also do: var CONST_WILD_TYPES = { REGULAR: 'RE', EXPANDING: 'EX', STICKY: 'ST', SHIFTING: 'SH' }; or even this: var CONST_WILD_TYPES = { REGULAR: '1', EXPANDING: '2', STICKY: '3', SHIFTING: '4' }; The last one, seems most efficient for string, it reduces your total bandwidth if you have server & client exchanging this data. Of course, now it's your duty to make sure there are no conflicts in the data (RE, EX, etc. must be unique, also 1, 2, etc. should be unique). Note that you need to maintain these forever for backward compatibility. Assignment: var wildType = CONST_WILD_TYPES.REGULAR; Comparision: if (wildType === CONST_WILD_TYPES.REGULAR) { // do something here }
I wasn't satisfied with any of the answers, so I made Yet Another Enum (YEA!). This implementation: uses more up-to-date JS requires just the declaration of this one class to easily create enums has mapping by name (colors.RED), string (colors["RED"]), and index (colors[0]), but you only need to pass in the strings as an array binds equivalent toString() and valueOf() functions to each enum object (if this is somehow not desired, one can simply remove it - small overhead for JS though) has optional global naming/storage by name string freezes the enum object once created so that it can't be modified Special thanks to Andre 'Fi''s answer for some inspiration. The codes: class Enums { static create({ name = undefined, items = [] }) { let newEnum = {}; newEnum.length = items.length; newEnum.items = items; for (let itemIndex in items) { //Map by name. newEnum[items[itemIndex]] = parseInt(itemIndex, 10); //Map by index. newEnum[parseInt(itemIndex, 10)] = items[itemIndex]; } newEnum.toString = Enums.enumToString.bind(newEnum); newEnum.valueOf = newEnum.toString; //Optional naming and global registration. if (name != undefined) { newEnum.name = name; Enums[name] = newEnum; } //Prevent modification of the enum object. Object.freeze(newEnum); return newEnum; } static enumToString() { return "Enum " + (this.name != undefined ? this.name + " " : "") + "[" + this.items.toString() + "]"; } } Usage: let colors = Enums.create({ name: "COLORS", items: [ "RED", "GREEN", "BLUE", "PORPLE" ] }); //Global access, if named. Enums.COLORS; colors.items; //Array(4) [ "RED", "GREEN", "BLUE", "PORPLE" ] colors.length; //4 colors.RED; //0 colors.GREEN; //1 colors.BLUE; //2 colors.PORPLE; //3 colors[0]; //"RED" colors[1]; //"GREEN" colors[2]; //"BLUE" colors[3]; //"PORPLE" colors.toString(); //"Enum COLORS [RED,GREEN,BLUE,PORPLE]" //Enum frozen, makes it a real enum. colors.RED = 9001; colors.RED; //0
Simplest solution: Create var Status = Object.freeze({ "Connecting":0, "Ready":1, "Loading":2, "Processing": 3 }); Get Value console.log(Status.Ready) // 1 Get Key console.log(Object.keys(Status)[Status.Ready]) // Ready
es7 way, (iterator, freeze), usage: const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar') for (let name of ThreeWiseMen) console.log(name) // with a given key let key = ThreeWiseMen.Melchior console.log(key in ThreeWiseMen) // true (string conversion, also true: 'Melchior' in ThreeWiseMen) for (let entry from key.enum) console.log(entry) // prevent alteration (throws TypeError in strict mode) ThreeWiseMen.Me = 'Me too!' ThreeWiseMen.Melchior.name = 'Foo' code: class EnumKey { constructor(props) { Object.freeze(Object.assign(this, props)) } toString() { return this.name } } export class Enum { constructor(...keys) { for (let [index, key] of keys.entries()) { Object.defineProperty(this, key, { value: new EnumKey({ name:key, index, enum:this }), enumerable: true, }) } Object.freeze(this) } *[Symbol.iterator]() { for (let key of Object.keys(this)) yield this[key] } toString() { return [...this].join(', ') } }
This can be useful: const [CATS, DOGS, BIRDS] = ENUM(); The implementation is simple and efficient: function * ENUM(count=1) { while(true) yield count++ } A generator can yield the exact sequence of integers required, without knowing how many constants there are. It can also support an optional argument that specifies which (possibly negative) number to start from (defaulting to 1).
A quick and simple way would be : var Colors = function(){ return { 'WHITE':0, 'BLACK':1, 'RED':2, 'GREEN':3 } }(); console.log(Colors.WHITE) //this prints out "0"
Here's a couple different ways to implement TypeScript enums. The easiest way is to just iterate over an object, adding inverted key-value pairs to the object. The only drawback is that you must manually set the value for each member. function _enum(list) { for (var key in list) { list[list[key] = list[key]] = key; } return Object.freeze(list); } var Color = _enum({ Red: 0, Green: 5, Blue: 2 }); // Color โ {0: "Red", 2: "Blue", 5: "Green", "Red": 0, "Green": 5, "Blue": 2} // Color.Red โ 0 // Color.Green โ 5 // Color.Blue โ 2 // Color[5] โ Green // Color.Blue > Color.Green โ false And here's a lodash mixin to create an enum using a string. While this version is a little bit more involved, it does the numbering automatically for you. All the lodash methods used in this example have a regular JavaScript equivalent, so you can easily switch them out if you want. function enum() { var key, val = -1, list = {}; _.reduce(_.toArray(arguments), function(result, kvp) { kvp = kvp.split("="); key = _.trim(kvp[0]); val = _.parseInt(kvp[1]) || ++val; result[result[val] = key] = val; return result; }, list); return Object.freeze(list); } // Add enum to lodash _.mixin({ "enum": enum }); var Color = _.enum( "Red", "Green", "Blue = 5", "Yellow", "Purple = 20", "Gray" ); // Color.Red โ 0 // Color.Green โ 1 // Color.Blue โ 5 // Color.Yellow โ 6 // Color.Purple โ 20 // Color.Gray โ 21 // Color[5] โ Blue
I've just published an NPM package gen_enum allows you to create Enum data structure in Javascript quickly: var genEnum = require('gen_enum'); var AppMode = genEnum('SIGN_UP, LOG_IN, FORGOT_PASSWORD'); var curMode = AppMode.LOG_IN; console.log(curMode.isLogIn()); // output true console.log(curMode.isSignUp()); // output false console.log(curMode.isForgotPassword()); // output false One nice thing about this little tool is in modern environment (including nodejs and IE 9+ browsers) the returned Enum object is immutable. For more information please checkout https://github.com/greenlaw110/enumjs Updates I obsolete gen_enum package and merge the function into constjs package, which provides more features including immutable objects, JSON string deserialization, string constants and bitmap generation etc. Checkout https://www.npmjs.com/package/constjs for more information To upgrade from gen_enum to constjs just change the statement var genEnum = require('gen_enum'); to var genEnum = require('constjs').enum;
I've made an Enum class that can fetch values AND names at O(1). It can also generate an Object Array containing all Names and Values. function Enum(obj) { // Names must be unique, Values do not. // Putting same values for different Names is risky for this implementation this._reserved = { _namesObj: {}, _objArr: [], _namesArr: [], _valuesArr: [], _selectOptionsHTML: "" }; for (k in obj) { if (obj.hasOwnProperty(k)) { this[k] = obj[k]; this._reserved._namesObj[obj[k]] = k; } } } (function () { this.GetName = function (val) { if (typeof this._reserved._namesObj[val] === "undefined") return null; return this._reserved._namesObj[val]; }; this.GetValue = function (name) { if (typeof this[name] === "undefined") return null; return this[name]; }; this.GetObjArr = function () { if (this._reserved._objArr.length == 0) { var arr = []; for (k in this) { if (this.hasOwnProperty(k)) if (k != "_reserved") arr.push({ Name: k, Value: this[k] }); } this._reserved._objArr = arr; } return this._reserved._objArr; }; this.GetNamesArr = function () { if (this._reserved._namesArr.length == 0) { var arr = []; for (k in this) { if (this.hasOwnProperty(k)) if (k != "_reserved") arr.push(k); } this._reserved._namesArr = arr; } return this._reserved._namesArr; }; this.GetValuesArr = function () { if (this._reserved._valuesArr.length == 0) { var arr = []; for (k in this) { if (this.hasOwnProperty(k)) if (k != "_reserved") arr.push(this[k]); } this._reserved._valuesArr = arr; } return this._reserved._valuesArr; }; this.GetSelectOptionsHTML = function () { if (this._reserved._selectOptionsHTML.length == 0) { var html = ""; for (k in this) { if (this.hasOwnProperty(k)) if (k != "_reserved") html += "<option value='" + this[k] + "'>" + k + "</option>"; } this._reserved._selectOptionsHTML = html; } return this._reserved._selectOptionsHTML; }; }).call(Enum.prototype); You can init'd it like this: var enum1 = new Enum({ item1: 0, item2: 1, item3: 2 }); To fetch a value (like Enums in C#): var val2 = enum1.item2; To fetch a name for a value (can be ambiguous when putting the same value for different names): var name1 = enum1.GetName(0); // "item1" To get an array with each name & value in an object: var arr = enum1.GetObjArr(); Will generate: [{ Name: "item1", Value: 0}, { ... }, ... ] You can also get the html select options readily: var html = enum1.GetSelectOptionsHTML(); Which holds: "<option value='0'>item1</option>..."
Even though only static methods (and not static properties) are supported in ES2015 (see here as well, ยง15.2.2.2), curiously you can use the below with Babel with the es2015 preset: class CellState { v: string; constructor(v: string) { this.v = v; Object.freeze(this); } static EMPTY = new CellState('e'); static OCCUPIED = new CellState('o'); static HIGHLIGHTED = new CellState('h'); static values = function(): Array<CellState> { const rv = []; rv.push(CellState.EMPTY); rv.push(CellState.OCCUPIED); rv.push(CellState.HIGHLIGHTED); return rv; } } Object.freeze(CellState); I found this to be working as expected even across modules (e.g. importing the CellState enum from another module) and also when I import a module using Webpack. The advantage this method has over most other answers is that you can use it alongside a static type checker (e.g. Flow) and you can assert, at development time using static type checking, that your variables, parameters, etc. are of the specific CellState "enum" rather than some other enum (which would be impossible to distinguish if you used generic objects or symbols). update The above code has a deficiency in that it allows one to create additional objects of type CellState (even though one can't assign them to the static fields of CellState since it's frozen). Still, the below more refined code offers the following advantages: no more objects of type CellState may be created you are guaranteed that no two enum instances are assigned the same code utility method to get the enum back from a string representation the values function that returns all instances of the enum does not have to create the return value in the above, manual (and error-prone) way. 'use strict'; class Status { constructor(code, displayName = code) { if (Status.INSTANCES.has(code)) throw new Error(`duplicate code value: [${code}]`); if (!Status.canCreateMoreInstances) throw new Error(`attempt to call constructor(${code}`+ `, ${displayName}) after all static instances have been created`); this.code = code; this.displayName = displayName; Object.freeze(this); Status.INSTANCES.set(this.code, this); } toString() { return `[code: ${this.code}, displayName: ${this.displayName}]`; } static INSTANCES = new Map(); static canCreateMoreInstances = true; // the values: static ARCHIVED = new Status('Archived'); static OBSERVED = new Status('Observed'); static SCHEDULED = new Status('Scheduled'); static UNOBSERVED = new Status('Unobserved'); static UNTRIGGERED = new Status('Untriggered'); static values = function() { return Array.from(Status.INSTANCES.values()); } static fromCode(code) { if (!Status.INSTANCES.has(code)) throw new Error(`unknown code: ${code}`); else return Status.INSTANCES.get(code); } } Status.canCreateMoreInstances = false; Object.freeze(Status); exports.Status = Status;
This is how Typescript translates it's enum into Javascript: var makeEnum = function(obj) { obj[ obj['Active'] = 1 ] = 'Active'; obj[ obj['Closed'] = 2 ] = 'Closed'; obj[ obj['Deleted'] = 3 ] = 'Deleted'; } Now: makeEnum( NewObj = {} ) // => {1: "Active", 2: "Closed", 3: "Deleted", Active: 1, Closed: 2, Deleted: 3} At first I was confused why obj[1] returns 'Active', but then realised that its dead simple - Assignment operator assigns value and then returns it: obj['foo'] = 1 // => 1
You can do something like this var Enum = (function(foo) { var EnumItem = function(item){ if(typeof item == "string"){ this.name = item; } else { this.name = item.name; } } EnumItem.prototype = new String("DEFAULT"); EnumItem.prototype.toString = function(){ return this.name; } EnumItem.prototype.equals = function(item){ if(typeof item == "string"){ return this.name == item; } else { return this == item && this.name == item.name; } } function Enum() { this.add.apply(this, arguments); Object.freeze(this); } Enum.prototype.add = function() { for (var i in arguments) { var enumItem = new EnumItem(arguments[i]); this[enumItem.name] = enumItem; } }; Enum.prototype.toList = function() { return Object.keys(this); }; foo.Enum = Enum; return Enum; })(this); var STATUS = new Enum("CLOSED","PENDING", { name : "CONFIRMED", ackd : true }); var STATE = new Enum("CLOSED","PENDING","CONFIRMED",{ name : "STARTED"},{ name : "PROCESSING"}); As defined in this library. https://github.com/webmodule/foo/blob/master/foo.js#L217 Complete example https://gist.github.com/lnt/bb13a2fd63cdb8bce85fd62965a20026