Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlphaHot/0071fb9ad295bace9ec52044b6960582 to your computer and use it in GitHub Desktop.
Save AlphaHot/0071fb9ad295bace9ec52044b6960582 to your computer and use it in GitHub Desktop.
Boolean Algebra in a nutshell for JS/TS programmers

Boolean Algebra in a nutshell for JS/TS programmers

There are a lot of strategies that you will hear about in the Javascript community for keeping your conditionals from becoming a tangled mess. This isn't like them. This is someting different. MATH! Boolean Algebra to be exact. I use it all the time to simplify complex conditionals. There are two things that you need to know: de Morgan's Theorem, and Karnaugh (pronounced CAR-no) Maps. (Don't worry, there is no test)

de Morgan's Theorem

De Morgan's Theorem is great distributing nots (!), and for when you want to convert an && to an ||, or back. This is it:

    !(A && B) = !A || !B
    !(A || B) = !A && !B

You sort of trade parentheses for switching the operation.

Karnaugh Maps

Karnaugh Maps are the real power!

Let's say that you have 4 variables that represent 4 conditions that you need to track, and you have to write different code for different branches.

I'm using the typescript syntax, but if this doesn't make sense, they are just four possible flags, and four functions that you need to run under certain conditions.

    declare const A: boolean
    declare const B: boolean
    declare const C: boolean
    declare const D: boolean

    declare function second(): void
    declare function second(): void
    declare function third(): void
    declare function fourth(): void

Now we need to chart out all of the possible conditions in a truth table. We make a little spreadsheet assigning each conditional to a row or column. We will use 1 for true and 0 for false because they only use one character and represent the same thing. Our setup would look like this to start with:

                AB 
          00 01 10 11
         +-----------
      00 | 
    C 01 |
    D 10 |
      11 | 

So the top entries would represent:

    00 = !A && !B
    01 = !A &&  B
    10 =  A && !B
    11 =  A &&  B

I assume everyone can figure out the same with C and D.

Back to our case:

Let's just fill some of this in. These are our business requirements in truth table form.

                         A B
             00      01      10      11
          ╔════════════════════════════════
          ║
       00 ║  first   first   second  second
          ║
    C  01 ║                  second  second
    D     ║
       10 ║          second  fourth  second
          ║
       11 ║  third   third   fourth        
          ║

That may help us visualize things a little, but it doesn't have too much power... yet! So this Karnaugh dude figured out this one weird trick to turn our truth table into something to help us write code. If we swap the order of 10 and 11, every row or column shift changes only one of your variables. It's going to let us reduce this mess!

                         A B
             00      01      11      10      
          ╔════════════════════════════════
          ║                                  
       00 ║  first   first   second  second  
          ║                                  
    C  01 ║                  second  second  
    D     ║                                  
       11 ║  third   third           fourth  
          ║                                  
       10 ║          second  second  fourth  
          ║

Now we play a little game. The goal is to make the biggest rectangles we can to cover a branch without covering any other branches. Here's the rules for our rectangles:

  • can expand up, down, left, right.
  • can wrap around to the other side of out spreadsheet Pac-Mac-style
  • are only allowed to take up 1, 2, or 4 cells in our spreadsheet on a side
  • may overlap
  • may cover voids
  • must contain at least one non-void cell
  • may only cover one branch at a time

That's the rules. Yeah, a 5th grader could handle this.

So in our case, I'll show each branch on it's own, which makes it pretty obvious how our rectandles are laid out (in this case becuase sometimes they can be more complex. It works the same. Go find a YouTube or something. I don't have time to type all of that out.) This is way easier to sketch out by hand, by the way.

So here are some candidates for the biggest rectangles (Go ahead. Look ahead. Just come right back, please.)

                         A B
             00      01      11      10      
          ╔════════════════════════════════
          ║ ┌─────────────┐                   
       00 ║ │first   first│   x      x       
          ║ │             │                   
    C  01 ║ │             │   x      x       
    D     ║ └─────────────┘                   
       11 ║  x       x               x       
          ║                                  
       10 ║          x       x       x       
          ║

Sooooo, back to code! Each cell is an and, between cells is or. Again, if you don't know Typescript, just ignore anything after a colon that looks to be in the wrong place.

    /**
     * Just a simple utility function to keep things tidy.
     */
    function when (condition: boolean, whenTrue: () => void): void {
      if (condition) {
        whenTrue()
      }
    }

    let firstCondition = 
      !A && !B && !C && !D || 
      !A &&  B && !C && !D ||
      !A && !B && !C &&  D || 
      !A &&  B && !C &&  D
        
    // !A and !C are in all four! Let's pull those outside of the parens...
    //
    //                    * Boolean Algebra! *

    firstCondition =
      !A && !C && 
      !B && !D ||
       B && !D ||
      !B &&  D || 
       B &&  D

    // Wait a minute... THAT IS JUST ALL OF THE COMBINATIONS OF B AND D!
    // So we don't care about those at all in the first branch, since they could 
    // be any value. Every time we increase a dimension on a rectangle, we're
    // eliminating a condition!
    //
    // See? This is the magic of those little rectangles. That Karnaugh
    // guy was a genious. 
    //
    // So where were we...? Oh yeah!
    //
    //                    * Boolean Algebra! *

    firstCondition = !A && !C

    // Wait a minute... that looks familair too... de Morgan's Theorem! Yep...
    //
    //                    * Boolean Algebra! *

    firstCondition = !(A || C) 
    
    // Maybe, or whichever you prefer. It's your code. I don't know your life.

Doing okay so far? Need to stretch or take a break? Go ahead. I'll wait here.

Back? Okay now that you got the basics of it, I'll skip being cute and explaining, but I'll still show my work like in middle school.

                         A B
             00      01      11      10      
          ╔════════════════════════════════
          ║                 ┌──────────────┐  
       00 ║  x       x      │second  second│ 
          ║                 │              │ 
    C  01 ║                 │second  second│ 
    D     ║                 └──────────────┘ 
       11 ║  x       x               x       
          ║         ┌──────────────┐                        
       10 ║         │second  second│ x       
          ║         └──────────────┘
    let secondCondition =
      //------Rectangle 0----    
      (  A &&  B && !C && !D ) || 
      (  A && !B && !C && !D ) ||
      (  A &&  B && !C &&  D ) ||
      (  A && !B && !C &&  D ) ||
      //------Rectangle 1----
      ( !A &&  B &&  C && !D ) ||
      (  A &&  B &&  C && !D )
    
    secondCondition =
      A && !C ||
      B && C && !D

You're doing great!

                         A B
             00      01      11      10      
          ╔════════════════════════════════
          ║                                  
       00 ║  x       x       x       x       
          ║ ┌─────────────┐                  
    C  01 ║ │             │  x       x       
    D     ║ │             │                                
       11 ║ │third   third│          x       
          ║ └─────────────┘                               
       10 ║          x       x       x       
          ║
    const thirdCondition = !A && C
                         A B
             00      01      11      10      
          ╔════════════════════════════════
          ║                                  
       00 ║  x       x       x       x       
          ║                                  
    C  01 ║                  x       x       
    D     ║                         ┌──────┐  
       11 ║  x       x              │fourth│ 
          ║                         │      │ 
       10 ║          x       x      │fourth│ 
          ║                         └──────┘
    const fourthCondition = A && !B && C

The Grand Finale

    when(firstCondition, first)
    when(secondCondition, second)
    when(thirdCondition, third)
    when(fourthCondition, fourth)

Isn't that pretty? Isn't that expressive? K-Maps made that so easy.

Now you can have more complex scenarios where there isn't a one best way. Sometimes you can do cool stuff where you can reduce your complexity by returning early. Remember that those voids were in there for convenience, so if you have a conditional in a void, it doesn't matter what its value is, so you might be able to simplify things that way. That's all conventional JS stuff. Lots of other members of our community have done a great job at explaining.

TL;DR

So yeah... Boolean algebra is magic. You should be a wizard and learn it. Don't be intimidated. It's all middle-school level math.

(I'm sure that there is an error or few in there. If anyone finds one, I'll try to edit and correct.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment