10

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.

5
  • If you have something set to STATE_B2, do you want a comparison to STATE_B to be true as well? Commented Jul 29, 2010 at 13:35
  • EndangeredMassa - yes absolutely I do, thanks for clarifying 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? 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? Commented Jul 29, 2010 at 13:55
  • The answer I provided should be able to do what you'd like. Commented Jul 29, 2010 at 14:34

5 Answers 5

7

What 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".

Sign up to request clarification or add additional context in comments.

2 Comments

I'd also like an evaluation of State.State_B.SUBSTATE_2 == State.State_B to evaluate to true. Any ideas on how to accomplish this?
@aeq: That comparison will never be true unless SUBSTATE_2 and State_B are the same objects.
5

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.

Comments

0

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.

2 Comments

I was confused for a while with my comments above. But say the state is State.STATE_B.B1: I do want a check for State.STATE_B to evaluate to true. How can I accomplish this? Thanks.
@aeq See the part after the note. Though you cannot evaluate with === anymore, but would have to use the check(value) method.
0

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

1 Comment

@TheCloudlessSky: Well, that's as close to comparison as you can get... :)
0
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.