• Uglify CSS Class names

    
    
    /**
     * @returns {string}
     */
    function getUniqueClassName() {
      getUniqueClassName.count = getUniqueClassName.count || 1;
    
      var helper = () => {
        var count = getUniqueClassName.count++;
        var ret = '';
        while (count > 0) {
          var curr = (count - 1) % 52;
          ret = String.fromCharCode((curr < 26) ? 97 /* a */ + curr : 65      /* A */ + (curr - 26)) + ret;
          count = Math.floor((count - 1) / 52);
        }
        return ret;
      }
    
      return helper();
    }
    
    getUniqueClassName.reset = function() {
      getUniqueClassName.count = 0;
    }
    
    console.log(getUniqueClassName()); // a
    console.log(getUniqueClassName()); // b
    
    
    

    Demo

  • HTMLElement to Virtual DOM and back

    /**
     * @param {HTMLElement} 
     * @return {object} object literal presentation
     */
    function virtualize(element) {
      if (element.nodeType === 3)
        return element.textContent;
      const ret = {
        type: element.tagName.toLowerCase(),
        props: {
          children: []
        }
      };
      element.childNodes.forEach(child => ret.props.children.push(virtualize(child)));
      if (ret.props.children.length === 1)
        ret.props.children = ret.props.children[0];
      for (const {
          name,
          value
        } of element.attributes)
        ret.props[name === 'class' ? 'className' : name] = value;
      return ret;
    }
    
    /**
     * @param {object} valid object literal presentation
     * @return {HTMLElement} 
     */
    function render(obj) {
      if (typeof obj === 'string') return document.createTextNode(obj);
      const {
        type,
        props: {
          children,
          ...attributes
        }
      } = obj;
      const ret = document.createElement(type);
      const childNodes = typeof children === 'string' ? [children] : children;
      childNodes.forEach(childNode => ret.append(render(childNode)));
      for (const [key, val] of Object.entries(attributes)) {
        ret.setAttribute(key === 'className' ? 'class' : key, val)
      }
      return ret;
    }

    Demo

  • Is Element in View

    <body onscroll="onscroll()">
    
      <div style="width: 400px; height:800px; border:1px solid #222;">Scroll Down</div>
    
      <div id="demo">
        Demo
      </div>
    
    </body>
    
    const isInView = (element) => {
      const {
        top,
        right,
        bottom,
        left
      } = element.getBoundingClientRect();
    
      return top >= 0 &&
        left >= 0 &&
        right <= (window.innerWidth || document.documentElement.clientWidth) &&
        bottom <= (window.innerHeight || document.documentElemetn.clientHeight);
    };
    
    const demo = document.getElementById('demo');
    
    const onscroll = function() {
      console.log(isInView(demo));
    };
    

    Demo

  • accessKey

    <div>Test</div>
    const demo = document.getElementById('demo');
    
     demo.onclick = (e) => {
       console.log(e.target.textContent);
     };
     demo.accessKey = 'w';
    
     // Alt + W for Windows + Chrome
     // [Control] [Option] + accesskey for Mac
     // Alt + W for Linux + Chrome

    Demo

  • Staircase using repeat()

    Runtime Complexity: O(n^2)

    /*Create a staircase based on user input n.
    
    steps(5) should return the below output:
    "#    "
    "##   "
    "###  "
    "#### "
    "#####" 
    */
    
    const steps = (n) => {
    if( n > 0){
      for (let row = 0; row < n; row++) {
          let step = '';
    
          for (let col = 0; col < n; col++) {
            col <= row ? step += '#' : step += ' ';
          }
           console.log(step);
       }
       return
    }
    console.log(`please give a number greater than 0`)
    }
    
    steps(5);
    /* Output
    "#    "
    "##   "
    "###  "
    "#### "
    "#####" */

    Demo

    String.prototype.repeat()

    Introduced in ES2015+, the repeat() method constructs and returns a new string which contains the specified number of copies of the string on which it was called, concatenated together.

    Syntax: str.repeat(count)
    count is an integer between 0 and infinity(positive) indicating the number of times to repeat the string.

    CodeOutput
    ‘abc’.repeat(-1)RangeError
    ‘abc’.repeat(0)
    ‘abc’.repeat(1)‘abc’
    ‘abc’.repeat(2)‘abcabc’
    ‘abc’.repeat(3.5)‘abcabcabc’ // Uses floor
    ‘abc’.repeat(1/0)Range Error

    Note: If the total length of the string to be returned equals or exceeds (1 << 28, i.e., 2^28) then, this method throws a RangeError as most modern browsers can’t handle strings longer than that.

    if((str.length * count) >= 1 << 28) // This 
    /* A simplified solution using String.prototype.repeat
     */
     
     const steps = (n) => {
      for(let i = 1; i <= n; i++) {
        let step = '#'.repeat(i) + ' '.repeat(n-i);
        console.log(step);
      }
    }
    
    steps(5);
    /* Output
    "#    "
    "##   "
    "###  "
    "#### "
    "#####" */

    Demo

  • Serialize / Deserialize

    You can do in this in many ways, the following code does this using prefix traversal.

    /**
     * @param {Node} root
     * @return {string}
     */
    function serialize(root) {
      if(!root) return '_';
      return `${root.val},${serialize(root.left)},${serialize(root.right)}`;
    }
    
    /**
     * @param {string} str
     * @return {Node}
     */
    function deserialize(str) {
      const q = str.split(',');
      return dfs(q);
    
     /**
     * @param {string} str
     * @return {Node}
     */
      function dfs(que) {
        if(!que.length) return null;
        const n = que.shift()
    ;
        if(n !== '_') {
          const node = new Node(n.value)
          node.left = dfs(q);
          node.right = dfs(q);
          return node;
        }
        return null;
      }
    }
  • DOM Elements as Keys in Object

    To store DOM Elements as keys in object without using WeakMap( ), we can do the following:

    <div id="root"></div>
    <script>
      class NodeStore {
        static key = 'key';
    
        set(node, value) {
          node.dataset[NodeStore.key] = value;
        }
    
        get(node) {
          return this.has(node) && node.dataset[NodeStore.key];
        }
    
        has(node) {
          return NodeStore.key in (node?.dataset || {});
        }
      }
    
      const nodeStore = new NodeStore();
      const root = document.getElementById('root');
      nodeStore.set(root, 'root value');
      console.log(nodeStore.has(root)); // true
      console.log(nodeStore.get(root)); // 'root value'
    </script>
    

    Demo

    However the above solution can only support values of type string. To support any type of value we can use the following solution:

    <div id="root"></div>
    <script>
    class NodeStore {
      static key = 'key';
      constructor() {
        this.map = {};
        this.counter = 0;
      }
       /**
       * @param {Node} node
       * @param {any} value
       */
      set(node, value) {
        node[NodeStore.key] = this.counter;
        this.map[this.counter++] = value;
      }
      /**
       * @param {Node} node
       * @return {any}
       */
      get(node) {
         if (this.has(node)) {
           return this.map[node[NodeStore.key]];
         }
         return;
       }
      
      /**
       * @param {Node} node
       * @return {Boolean}
       */
      has(node) {
        return NodeStore.key in node;
      }
    }
    const nodeStore = new NodeStore();
    const root = document.getElementById('root');
    nodeStore.set(root, 22);
    console.log(nodeStore.has(root)); // true
    console.log(nodeStore.get(root)); // 22
    </script>

    Demo

  • Generator Functions in JavaScript:

    Introduction

    ES6 introduced new type of functions called Generator Functions. A function keyword with an asterisk * is used to define a generator function.

    function* generatorFunction(i) {
      yield i;
      yield i + 10;
    }
    const generator = generatorFunction(1);
    console.log(generator.next().value); //1
    console.log(generator.next().value); //11

    Demo

    A Generator Function can be called as many times as desired. Every time the function is called it returns a special type of iterator object called Generator. Generators are unique, calling the function by next() method returns a new Generator. The next() method returns an object with a Value property containing the yield value and a done property which is a Boolean indicating whether the generator has yielded its last value.

    //Normal functions 
    
    function normalFunction() {
      console.log("cannot");
      console.log("be");
      console.log("stopped");
    }
    
    normalFunction()// cannot be stopped
    
    //Generator function
    
    function* generatorFunction() {
      yield console.log("can");
      yield console.log("be");
      yield console.log("stopped");
    }
    
    const generator = generatorFunction();
    generator.next().value; // can
    generator.next().value; // be
    generator.next().value; // stopped

    Demo

    Memory Efficient

    Generator Functions are memory efficient, they can be stopped midway or suspend function execution to yield values on demand and continue from where it was stopped by calling next().

    They are efficient when iterating over a large data set(or an infinite list). Most of the time we may not want to iterate through the full list and want to generate only what is needed at that time.

    function* generatorFunction(max) {
        let number = 0;
    
        //iterate over an infinite count to generate squared numbers
    
        while (number < max) {
          number++;
          yield number * number;
        }
      }
    
      const max = Infinity;
    
      let iterationCount = 0;
      
      const totalIterations = 5;
    
      const squaredNumber = generatorFunction(max);
    
      //generate first 5 squared numbers
      
      while (iterationCount < totalIterations) {
        iterationCount++;
        console.log(squaredNumber.next().value);
      }
    

    Demo

    Using Return in the Generator Function

    When the return statement gets executed then the done property will be set to true. Any subsequent next() method will not be executed. Note that any error that is not caught in the function execution will finish the function as well and returns.

    function* generatorWithReturn(i) {
      yield i; //returns i
      return i; //returns i
      yield i; //returns undefined
    }
    
    var gen = generatorWithReturn("conitnue")
    console.log(gen.next()); // { value: "continue", done: false }
    console.log(gen.next()); // { value: continue, done: true }
    console.log(gen.next()); // { value: undefined, done: true }

    Demo

  • Alien Dictionary

    Given a list of words (Strings), generate the order of the language. If the given input is invalid you can return an empty string (”). Sometimes, multiple orders are possible. The following approach uses Topological Sort and DFS (Depth First Search).

    const buildAdjacencyList = (wordList, adjacencyList) => {
       for (const word of wordList) {
         for (let i = 0; i < word.length; i++) {
           if (!adjacencyList.has(word.charAt(i))) {
             adjacencyList.set(word.charAt(i), []);
           }
         }
       }
    
     for (let i = 0; i < wordList.length - 1; i++) {              
         const firstWord = wordList[i];
         const secondWord = wordList[i + 1];
         if (firstWord.startsWith(secondWord) && firstWord.length > secondWord.length) {
           return false;
          }
         const minLen = Math.min(firstWord.length, secondWord.length);
         for (let j = 0; j < minLen; j++) {
           const firstWordChar = firstWord.charAt(j);
           const secondWordChar = secondWord.charAt(j);
           if (firstWordChar !== secondWordChar) {
             // Store in reverse to get the correct order in the end.
             adjacencyList.get(secondWordChar).push(firstWordChar);
             break;
           }
         }
       }
       return true;
     };
    
     const topologicalSort = (currChar, adjacencyList, visited, resultStack) => {
       if (!(currChar in visited)) {
         visited[currChar] = false;
         if (adjacencyList.has(currChar)) {
           for (const char of adjacencyList.get(currChar)) {
             const result = topologicalSort(char, adjacencyList, visited, resultStack);
             if (!result) return false;
           }
         }
         visited[currChar] = true;
         resultStack.push(currChar);
         return true;
       } else {
                return visited[currChar];
               }
     };
    
     var alienOrder = function(words) {
       const adjacencyList = new Map();
       const valid = buildAdjacencyList(words, adjacencyList);
       if (!valid) {
         return '';
       }
       const resultStack = [];
       const visited = {};
       for (const [key, value] of adjacencyList) {
         const result = topologicalSort(key, adjacencyList, visited, resultStack);
         if (!result) {
           return '';
         }
       };
       if (resultStack.length !== adjacencyList.size) {
         return '';
       }
       return resultStack.join('');
     };
    
     console.log(alienOrder(['dog', 'dot', 'dotted']));
     // "dogte"

    Demo

  • Maximum height by stacking Cuboids

    Box stacking problem max height of stack possible (can be rotated). All the dimensions of the box beneath should be greater than the one stacked above it.

    box-stacking

    Javascript

    const maxHeight = (cuboids) => {
      // Sort each cuboid dimensions.
      for (const cuboid of cuboids) {
        cuboid.sort((a, b) => a - b);
      }
      
      // Sort the cuboids from largest to smallest order.
      cuboids.sort((a, b) => {
        if (a[0] != b[0]) {
          return b[0] - a[0];
        }
        if (a[1] != b[1]) {
          return b[1] - a[1];
        }
        return b[2] - a[2];
      });
      
      // dp[i] means max height upto cuboid 'i' (row i).
      const dp = Array(cuboids.length);
      
      for (let i = 0; i < cuboids.length; i++) {
        // We choose the largest side of cuboid to be height to maximize it.
        dp[i] = cuboids[i][2];
        for (let j = 0; j < i; j++) {
          if (cuboids[j][0] >= cuboids[i][0] && cuboids[j][1] >= cuboids[i][1] && cuboids[j][2] >= cuboids[i][2]) {
            dp[i] = Math.max(dp[i], dp[j] + cuboids[i][2]);
          }
        }
      }
      
      return Math.max(...dp);
    }
    
    console.log(maxHeight([
      [1, 1, 1 ],
      [2, 3, 10],
      [2, 4, 1 ],
    ]));
    // 15

    Demo