Search code examples
javascriptarraysbitwise-operatorsbitmask

Bitwise operators in JavaScript as flags on a state


I am trying to install some logic into a piece of JS I am working on that will simply add/remove flags depending on some conditions.

It primarily consists of two steps

  1. Build object based on some stateful data
  2. Hand off to post initialization method to parse data array of objects

In Step 1, there is a 'status' property, which can have a number of flags:

  - INCOMPLETE      = 1
  - COMPLETE        = 2
  - CURRENT         = 4
  - NOT_APPLICABLE  = 8

When the object array is parsed, it checks each item to see what status flags are set:

  if CURRENT is set, give class 'item-current' to node
  if INCOMPLETE is set, give class 'item-incomplete' to node
  if COMPLETE is set, give class 'item-complete' to node
  if NOT_APPLICABLE is set, give class 'item-not-applicable' to node

As the user interacts with the page, the flags will change on the object array - when a user 'completes' a segment, the status for that object property will remove the INCOMPLETE flag, and add the COMPLETE flag.

I tried doing this this way:

   let v = [
       {
         name: "Item 1",
         status: COMPLETE
       },
       {
         name: "Item 2",
         status: INCOMPLETE | CURRENT
       },
       {
         name: "Item 3",
         status: INCOMPLETE
       }
   ];

   // Moving on to step 3

   v[1].status = v[1].status ^ INCOMPLETE ^ CURRENT | COMPLETE; // remove incomplete and current from Item 2, marking as COMPLETE
   v[2].status = v[2].status | CURRENT;  // marks Item 3 as current step, but not yet complete

In the above scenario, Item 1 has been completed, and the user has moved on to Item 2 which is still INCOMPLETE, and CURRENT step. For some reason, when I am evaluating the conditions of the flags, it gives me (what seems like) unexpected behavior.

I have tried removing the flag by doing these: v[1].status ^= CURRENT v[1].status = v[1].status ^ CURRENT

And comparing by:

if ((v[1].status & COMPLETE) === COMPLETE) {
  // is complete
} else {
  // not complete
}

But yet I still seem to face flags seeming like they are still set, or unexpectedly getting set. Is there an easier way to do this?

The desired result, would be a case where I can set the flag, remove the flag, and check to see if the flag is set - using constants that are bitmasks.

Why is INCOMPLETE | CURRENT the same thing as INCOMPLETE ^ CURRENT?

I am struggling with removing a bit from the mask and returning that value it seems.


Solution

  • You can't use bitwise operations like this with values that are not powers of 2.

    The value 3 could be any of the follwing values:

    • CURRENT
    • COMPLETE | INCOMPLETE
    • CURRENT | COMPLETE
    • CURRENT | INCOMPLETE
    • CURRENT | COMPLETE | INCOMPLETE

    There's no way to tell which combination it is.

    Change the values to

    • COMPLETE = 1
    • COMPLETE = 2
    • CURRENT = 4
    • NOT_APPLICABLE = 8

    and it's so easy to do bitwise operations with powers of 2.