Skip to content

Instantly share code, notes, and snippets.

@Ustice
Last active January 9, 2024 20:16
Show Gist options
  • Save Ustice/5c03c9077163cabd93f3becab2635986 to your computer and use it in GitHub Desktop.
Save Ustice/5c03c9077163cabd93f3becab2635986 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 first(): 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.

    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

    if (firstCndition) {
      return first()
    }

    if (secondCondition) {
      return second()
    }

    if (thirdCondition) {
      return third()
    }

    if (fourthCndition) {
      return 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.)

@kenzhemir
Copy link

Hey!

Noticed small typo

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

declare function second(): void // <-- should be first, right?
declare function second(): void
declare function third(): void
declare function fourth(): void

@shuckster
Copy link

One more typo:

if (condition) {
  whenTrue // <-- should be applied with (), right?
}

@Ustice
Copy link
Author

Ustice commented Feb 27, 2023

Thank you! Yep. This is a great example of how important it is to test your code. 😅

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