npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tuples-boolean-bitmasker

v1.0.2

Published

Utility to use tuples of booleans (i.e. array of booleans) as filtering mechanism leveraging Bitwise operators 'under the hood'

Downloads

100

Readme

tuples-boolean-bitmasker

Use fixed length array of boolean as input for comparsion system.

| Version | Size | Dependencies | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | npm | npm bundle size | Libraries.io dependency status for latest release |

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
const displayMap: Record<string, [boolean, boolean, boolean]> = {
  phoneNumber: [false, true, false],
}

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.

import BitMasker from 'tuples-boolean-bitmasker'

// 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
const displayMap: Record<string, BitMasker> = {
  phoneNumber: new BitMasker([false, true, false]),
}

// But when it comes to what's sensitive to the current user
// We can do this dynamically
const CURRENT_USER = new BitMasker([true, true, false]) // 0b110

// 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 present
0b010 & 0b110 // 2
// Using BitMasker includes would do the same,
displayMap['phoneNumber'].includes(CURRENT_USER) // true

// Or the opposite
const ANOTHER_USER = new BitMasker([false, false, true])
0b010 & ANOTHER_USER.toNumber() // 0
displayMap['phoneNumber'].includes(ANOTHER_USER) // false

With BitMasker you can use both methods interchangeably.

Bookmarks

Links that helped me prototype