I have the following "Enum" in javascript to indicate the state of my application:
var State = {
STATE_A: 0,
STATE_B: 1,
STATE_C: 2
//...
}
Now, I want each state to have a "sub-state". So for example, STATE_B
could be in STATE_B1
or STATE_B2
...
What would be the best way to structure this? Would I somehow nest an "enum" within the State
"enum" ? Thanks
If there is a better way to structure this altogether (other than enums) I'm all ears. Basically I need to be able to set and check the state of my application, and each state can (but not necessary) have a sub-state attached to it which can be set and checked. Even better if the solution allows me to go more than 1 level of nesting deep.
I have the following "Enum" in javascript to indicate the state of my application:
var State = {
STATE_A: 0,
STATE_B: 1,
STATE_C: 2
//...
}
Now, I want each state to have a "sub-state". So for example, STATE_B
could be in STATE_B1
or STATE_B2
...
What would be the best way to structure this? Would I somehow nest an "enum" within the State
"enum" ? Thanks
If there is a better way to structure this altogether (other than enums) I'm all ears. Basically I need to be able to set and check the state of my application, and each state can (but not necessary) have a sub-state attached to it which can be set and checked. Even better if the solution allows me to go more than 1 level of nesting deep.
Share Improve this question edited Jul 29, 2010 at 13:30 aeq asked Jul 29, 2010 at 13:17 aeqaeq 10.5k16 gold badges46 silver badges46 bronze badges 5- If you have something set to STATE_B2, do you want a comparison to STATE_B to be true as well? – EndangeredMassa Commented Jul 29, 2010 at 13:35
- EndangeredMassa - yes absolutely I do, thanks for clarifying – aeq Commented Jul 29, 2010 at 13:37
- Random thought: Some answers popping up with objects, but do they address the STATE_B === 1 as well as the STATE_B.B1 === 1? – Mark Schultheiss Commented Jul 29, 2010 at 13:46
- back to EndangeredMassas point (after thinking about it, it turned out to be a very important question): I do, in fact, want this comparison to be true. I don't think the solutions below allow for this though. any ideas? – aeq Commented Jul 29, 2010 at 13:55
- The answer I provided should be able to do what you'd like. – TheCloudlessSky Commented Jul 29, 2010 at 14:34
5 Answers
Reset to default 7What you're doing isn't really enums. You're using native Javascript objects and just treating them like enums, which is perfectly acceptable when you want an enum-like object in Javascript.
To answer your question, yes, you can totally nest objects:
var State = {
STATE_A: 0,
STATE_B:{
SUBSTATE_1 : "active",
SUBSTATE_2 : "inactive"
},
STATE_C: 2
//...
}
You then just use the dot notation in order to set those values, like
State.State_B.SUBSTATE_2 = "active"
.
You could use some sort of bit-field if you want:
var State = function() {
// Note this must increment in powers of 2.
var subStates = 8;
var A = 1 << subStates;
var B = 2 << subStates;
var C = 4 << subStates;
var D = 8 << subStates;
return {
// A
// Note this must increment in powers of 2.
STATE_A: A,
STATE_A1: A | 1,
STATE_A2: A | 2,
STATE_A3: A | 4,
STATE_A4: A | 8,
STATE_A5: A | 16
// B
STATE_B: B,
STATE_B1: B | 1,
// C
STATE_C: C,
STATE_C1: C | 1,
STATE_C2: C | 2,
STATE_C3: C | 4,
STATE_C4: C | 8,
// D
STATE_D: D
};
}();
// Set a state.
var x = State.STATE_A1; // Same as State.STATE_A | State.STATE_A1
// Determine if x has root state of A?
if(x & State.STATE_A == State.STATE_A) {
console.log("Root state is A.");
}
else {
console.log("Nope, not root state A.");
}
// Determine if x has sub-state A1?
if(x & State.STATE_A1 == State.STATE_A1) {
console.log("A with Substate 1");
}
So the first 8 bits are reserved for setting the sub-state. You could, of course, increase this as long as the root-state and sub-state can fit inside a 32-bit integer. If you need explanation as to why/how this works (bit-wise operators), let me know.
I guess you want to write something like
if (State.STATE_A === someState) { ... }
You could simply define another layer in your State object like
var State = {
STATE_A : 0
STATE_B : {
B1 : 1,
B2 : 2,
}
};
...
if (State.STATE_B.B1 == someState){...}
Edit: Based on the comments on your question another approach could be this.
//Creates state objects from you json.
function createStates(json) {
var result = {};
for(var key in json) {
result[key] = new State(json[key]);
}
return result;
}
//State class
function State(value) {
//If the state value is an atomic type, we can do a simple comparison.
if (typeof value !== "object") {
this.value = value;
this.check = function(comp){ return value === comp; };
}
// Or else we have more substates and need to check all substates
else if (typeof value === "object") {
this.value = createStates(value);
for(var key in this.value) {
//Allows to access StateA.SubStateA1. Could really mess things up :(
this[key] = this.value[key];
}
this.check = function(comp){
for(var key in this.value) {
if (this.value[key].check(comp) === true){
return true;
}
}
return false;
};
}
};
Now you can call everything with
var stateJson = {
STATE_A : 0,
STATE_B : {
B1 : 1,
B2 : 2
}
};
var states = createStates(stateJson);
alert(states.stateA.check(0)); // Should give true
alert(states.STATE_B.B1.check(1)); // Same here
alert(states.STATE_B.check(1)); //And again because value is valid for one of the substates.
Since JavaScript does not support operator overloading, you cannot directly test for equality of substates using the ==
operator. The closest you can get is to use the instanceof
operator to check if a state is of a given type, for example:
// All these functions are empty because we only need the type and there is no data
function State() {
}
function State_A() {
}
State_A.prototype = new State();
function State_B() {
}
State_B.prototype = new State();
function State_B1() {
}
State_B1.prototype = new State_B();
function State_B2() {
}
State_B2.prototype = new State_B();
And since functions are also objects, you can add your nesting right into the State
function:
State.STATE_A = new State_A();
State.STATE_B = new State_B();
State.STATE_B.STATE_B1 = new State_B1();
State.STATE_B.STATE_B2 = new State_B2();
And check its type:
var myState = State.STATE_B1;
myState instanceof State // true
myState instanceof State_A // false
myState instanceof State_B // true
myState instanceof State_B1 // true
function State () {
this.superState = null;
}
State.prototype = {
constructor: State
, mkSubState () {
var subState = new State ();
subState.superState = this;
return subState;
}
, isSubStateOf (superState) {
var state = this;
while (state !== null) {
if (this.superState === superState) {
return true;
}
state = this.superState;
}
return false;
}
, isSuperStateOf (subState) {
while (subState !== null) {
if (subState.superState === this) {
return true;
}
subState = subState.superState;
}
return false;
}
};
var States = {};
States.A = new State ();
States.A1 = States.A.mkSubState ();
States.A2 = States.A1.mkSubState ();
States.B = new State ();
States.B1 = States.B.mkSubState ();
States.B2 = States.B1.mkSubState ();
States.B2.isSubStateOf (B); // true
States.B2.isSubStateOf (B1); // true
States.B2.isSubStateOf (B2); // false
States.B2.isSubStateOf (A); // false
States.B2.isSubStateOf (A1); // false
States.B2.isSubStateOf (A2); // false