tuples-boolean-bitmasker
Use fixed length array of boolean as input for comparsion system.
Version | Size | Dependencies |
---|---|---|
Ever had to take a known length ordered set of booleans and wanted to compare if any item would end up checking essentially for true === true
in multi-dimensional arrays as a way of filtering?
If this happened, maybe you've wondered if there were a quicker way?
There is, and it’s referenced in computer science terminology as a "Bit mask" and typically use "Bitwise" operators where we use numbers that are internally represented in binary and can mutate numbers using "Bitwise" operators such as shift bit, or inverse them, or compare against something ("Bit mask").
Binary? (a refresher)
(Disclaimer: The author is not a computer scientist, this a non exhaustive attempt at vulgarization, parts may be left out)
This is to illustrate how we can take a known set of booleans for any purpose, and how we can convert into numbers as a simpler way of doing filtering.
In binary, any number are sequence of 1
and 0
(a.k.a. base 2), any number is broken down like this. For example the number 22
(as decimal, or base 10, like we write it) in ECMAScript you can know by adding the radix argument (what numeral base) to the toString method; Number(22).toString(2)
= 10110
;
Decimal 22
in binary is 10110
. Binary reads from right to left. Any 0
found when reading left-to-right are ignored too. It's helpful to us non-computers, but 0
is always ignored. Each 1
has a meaning in relation to where it is. Each slot as a value at the power of two 2n (that's where "base 2" is from).
So 10110
is broken down as 24 + 0 + 22 + 21 + 0
, or 16 + 0 + 4 + 2 + 0
.
We could say that each binary slot have a meaning. As if 1
stands for true.
A "Bit mask" can be used as a query mechanism.
A "Bitwise" operation is a mutation mechanism to query.
Bitwise and Bit masks
We can use the numbers as way of storing enable/disable per bit, and we can use this library as an aid to visualize and also to make operations.
If we were to convert numbers in binary, and ask if there is a collision between the two, we'd use a "Bitwise" operator against each other:
101 -> Here's what I can do, I'm 5, my bits are flipped "on" at slot 1 and 4.
100 -> I'm just checkin' if you have bit 4 "on"
So the 101
(or 0b101
) could be seen as a "Mask", from which we can compare against something else. Say 100
would be just before doing an action, checking if current user has its matching flag "on".
In that context, we want to check if both have something in common ("Logical AND"), the "Bitwise" walks each "slot" and sees if there is a bit flipped on (i.e. 1
instead of 0
) successively at each slot from right to left.
In JavaScript, you'd do;
0b100 & 0b101 // 4
But what when there is no collision
100 -> Here's what I can do, I'm 4, my bits are flipped "on" only at slot 4
001 -> I'm just checkin' if you have bit 1 "on"
In JavaScript, you'd do
0b100 & 0b001 // 0
No collision, not enabled.
There are more use-cases for Bit masks and Bitwise operations than Logical AND, other use-cases can be used with this BitMasker and goes beyond the scope of this document.
Lastly, about Bit masking and Bitwise, as previously said, this principle is not new. It is common in low-level software (e.g. Linux Kernel, Networking, etc.). For example, the Linux filesystem modeled their File System permissions using a similar model. You can read more on Wikipedia about it.
Usage in the context of a Web Application
Now imagine your Redux or RxJS, or Vuex, or any application state system that contains values with booleans in them.
Sometimes we have requirements that asks to display data based on those booleans.
Requirements that might look like;
Artist's profile fields visibility
Field name | Casting agent | Artist | Anonymous |
---|---|---|---|
phoneNumber | N | Y | N |
fullName | N | Y | Y |
artistName | Y | Y | Y |
And we have to walk through many objects to create an Artist data object Record.
For each field, we'd have to check if we can display it and we would manually write array of arrays and iterate.
How about we could tell, "display phone number when X"
// But that can get out of hand quickly
While storing mapping for hide/show mapping can be done in many ways, booleans feels nice. And is readable.
But, the faster way of covering all possible combinations that almost always been available in computes can be used.
Documentation about it feels a bit like dark magic, here's how we can help ourselves and use both methods together.
// displayMap would normally be built once per app startup// Field visibility mapping should not change dynamically// This could even come from a static JSON file loaded at App startup // But when it comes to what's sensitive to the current user// We can do this dynamically // Under the hood, BitMasker helps doing bitwise logic// So we don't have to write things like this // If we want to check if we should do something, we can use & operator // If we get a non-zero, it means the 3rd bit is present0b010 & 0b110 // 2// Using BitMasker includes would do the same,displayMap.includesCURRENT_USER // true // Or the opposite0b010 & ANOTHER_USER.toNumber // 0displayMap.includesANOTHER_USER // false
With BitMasker you can use both methods interchangeably.
Bookmarks
Links that helped me prototype
- Dr Axel Rauschmayer’s Seaking JS book, Chapter 11. Numbers
- Dr Axel Rauschmayer’s Exploring ES6 book, chapter New number and Math features
- Config flags section in Interesting use cases for JavaScript bitwise operators
- Dev.To Bitmasks: A very esoteric (and impractical) way of managing booleans
- StackOverflow How to output numbers with leading zeros in JavaScript
- StackOverflow Create variable width bitmasks
- StackOverflow Convert “boolean bit array” to number
- StackOverflow How to convert text to binary code in JavaScript?
- StackOverflow Is there “0b” or something similar to represent a binary number in Javascript