Categories
interview

Event Emitter pattern

Event emitter pattern implemented using a Javascript ES6 class.

class EventEmitter {
  constructor() {
    this.events = {};
  }

  subscribe = (event, cb) => {
    (this.events[event] || (this.events[event] = [])).push(cb);
    return {
      unsubscribe: () => {
        (this.events[event] && this.events[event]).splice(this.events[event].indexOf(cb) >>> 0, 1);
      }
    };
  };

  emit = (event, ...rest) => {
    (this.events[event] || []).forEach(val => {
      val(...rest);
    });
  };
}

const eventEmitter = new EventEmitter();
const ret1 = eventEmitter.subscribe('test', () => {
  console.log('test1');
});
const ret2 = eventEmitter.subscribe('test', () => {
  console.log('test2');
});
const ret3 = eventEmitter.subscribe('test3', () => {
  console.log('test3');
});
t.emit('test'); // test1, test2
ret1.unsubscribe();
ret1.unsubscribe(); // Should still function as expected.
t.emit('test'); // test2

Demo

Categories
interview

Employee badging times – Javascript

We are working on a security system for a badged-access room in our company’s building.

We want to find employees who badged into our secured room unusually often. We have an unordered list of names and entry times over a single day. Access times are given as numbers up to four digits in length using 24-hour time, such as “800” or “2250”.

Write a function that finds anyone who badged into the room three or more times in a one-hour period. Your function should return each of the employees who fit that criteria, plus the times that they badged in during the one-hour period. If there are multiple one-hour periods where this was true for an employee, just return the earliest one for that employee.

badge_times = [
[‘Paul’, ‘1355’],
[‘Jennifer’, ‘1910’],
[‘Jose’, ‘835’],
[‘Jose’, ‘830’],
[‘Paul’, ‘1315’],
[‘Chloe’, ‘0’],
[‘Chloe’, ‘1910’],
[‘Jose’, ‘1615’],
[‘Jose’, ‘1640’],
[‘Paul’, ‘1405’],
[‘Jose’, ‘855’],
[‘Jose’, ‘930’],
[‘Jose’, ‘915’],
[‘Jose’, ‘730’],
[‘Jose’, ‘940’],
[‘Jennifer’, ‘1335’],
[‘Jennifer’, ‘730’],
[‘Jose’, ‘1630’],
[‘Jennifer’, ‘5’],
[‘Chloe’, ‘1909’],
[‘Zhang’, ‘1’],
[‘Zhang’, ’10’],
[‘Zhang’, ‘109’],
[‘Zhang’, ‘110’],
[‘Amos’, ‘1’],
[‘Amos’, ‘2’],
[‘Amos’, ‘400’],
[‘Amos’, ‘500’],
[‘Amos’, ‘503’],
[‘Amos’, ‘504’],
[‘Amos’, ‘601’],
[‘Amos’, ‘602’],
[‘Paul’, ‘1416’],
];

Expected output (in any order)

{
Paul: [‘1315’, ‘1355’, ‘1405’],
Jose: [‘830’, ‘835’, ‘855’, ‘915’, ‘930’],
Zhang: [’10’, ‘109’, ‘110’],
Amos: [‘500’, ‘503’, ‘504’],
}
n: length of the badge records array.

const badge_records = [
  ['Paul', '1355'],
  ['Jennifer', '1910'],
  ['Jose', '835'],
  ['Jose', '830'],
  ['Paul', '1315'],
  ['Chloe', '0'],
  ['Chloe', '1910'],
  ['Jose', '1615'],
  ['Jose', '1640'],
  ['Paul', '1405'],
  ['Jose', '855'],
  ['Jose', '930'],
  ['Jose', '915'],
  ['Jose', '730'],
  ['Jose', '940'],
  ['Jennifer', '1335'],
  ['Jennifer', '730'],
  ['Jose', '1630'],
  ['Jennifer', '5'],
  ['Chloe', '1909'],
  ['Zhang', '1'],
  ['Zhang', '10'],
  ['Zhang', '109'],
  ['Zhang', '110'],
  ['Amos', '1'],
  ['Amos', '2'],
  ['Amos', '400'],
  ['Amos', '500'],
  ['Amos', '503'],
  ['Amos', '504'],
  ['Amos', '601'],
  ['Amos', '602'],
  ['Paul', '1416']
];

const getOftenUsersOfBadge = (records) => {
  records.sort((a, b) => a[1] - b[1]);
  const obj = {};
  for (const [name, time] of records) {
    (obj[name] || (obj[name] = [])).push(time);
  }
  const result = {};
  for (const [key, val] of Object.entries(obj)) {
    const len = val.length;
    if (len >= 3) {
      for (let i = 2; i < len; i++) {
        if (val[i] - val[i - 2] <= 100) {
          const start = i - 2;
          const max = parseInt(val[start]) + 100;
          while (parseInt(val[++i]) <= max) {}
          result[key] = val.slice(start, i);
          break;
        }
      }
    }
  }
  return result;
};

console.log(getOftenUsersOfBadge(badge_records));

/* Explanation:
1) Sort the input records based on times in increasing order.
2) Build a map { name : [time1, time2,  ... timen], }
3) Computing times
          1 --> 00:01
        101 --> 01:01
        diff is 100 for 1 hour.
    No need to check (i < len) in the while loop as we break the loop on first failure of condition.
*/

Demo

Categories
interview

2D Array JavaScript (ES6)

const twoDarray = Array(3).fill(0).map(x => Array(3).fill(0));

console.log(twoDarray);
// [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

Demo

Categories
interview

Find mismatches in badging records

Given an ordered list of employees who used their badge to enter or exit the room, write a function that returns two collections:
1. All employees who didn’t use their badge while exiting the room – they recorded an enter without a matching exit. (All employees are required to leave the room before the log ends.)
2. All employees who didn’t use their badge while entering the room – they recorded an exit without a matching enter. (The room is empty when the log begins.)
Each collection should contain no duplicates, regardless of how many times a given employee matches the criteria for belonging to it.

records1 = [
	  ["Martha",   "exit"],
	  ["Paul",     "enter"],
	  ["Martha",   "enter"],
	  ["Steve",    "enter"],
	  ["Martha",   "exit"],
	  ["Jennifer", "enter"],
	  ["Paul",     "enter"],
	  ["Curtis",   "exit"],
	  ["Curtis",   "enter"],
	  ["Paul",     "exit"],
	  ["Martha",   "enter"],
	  ["Martha",   "exit"],
	  ["Jennifer", "exit"],
	  ["Paul",     "enter"],
	  ["Paul",     "enter"],
	  ["Martha",   "exit"],
	  ["Paul",     "enter"],
	  ["Paul",     "enter"],
	  ["Paul",     "exit"],
	  ["Paul",     "exit"] 
	]

Expected output: ["Paul", "Curtis", "Steve"], ["Martha", "Curtis", "Paul"]

Other test cases:

records2 = [
	  ["Paul", "enter"],
	  ["Paul", "exit"],
	]

Expected output: [], []

records3 = [
	  ["Paul", "enter"],
	  ["Paul", "enter"],
	  ["Paul", "exit"],
	  ["Paul", "exit"],
	]

Expected output: ["Paul"], ["Paul"]

records4 = [
	  ["Paul", "enter"],
	  ["Paul", "exit"],
	  ["Paul", "exit"],
	  ["Paul", "enter"],
	]

Expected output: ["Paul"], ["Paul"]
const mismatches = (records) => {
  const obj = {};
  const notExited = [];
  const notEntered = [];
  for (const [name, state] of records) {
    if (!(name in obj)) {
      obj[name] = 0;
    }

    if (state === 'enter') {
      obj[name]++;
    } else {
      obj[name]--;
    }

    if (obj[name] > 1) {
      if (!notExited.includes(name)) {
        notExited.push(name);
      }
      obj[name] = 0;
    }
    if (obj[name] < 0) {
      if (!notEntered.includes(name)) {
        notEntered.push(name);
      }
      obj[name] = 0;
    }
  }

  for (const [key, val] of Object.entries(obj)) {
    if (val === 1) {
      if (!notExited.includes(key)) {
        notExited.push(key);
      }
    }
  }
  return [notExited, notEntered];
};

console.log(mismatches([
    ["Martha", "exit"],
    ["Paul", "enter"],
    ["Martha", "enter"],
    ["Steve", "enter"],
    ["Martha", "exit"],
    ["Jennifer", "enter"],
    ["Paul", "enter"],
    ["Curtis", "exit"],
    ["Curtis", "enter"],
    ["Paul", "exit"],
    ["Martha", "enter"],
    ["Martha", "exit"],
    ["Jennifer", "exit"],
    ["Paul", "enter"],
    ["Paul", "enter"],
    ["Martha", "exit"],
    ["Paul", "enter"],
    ["Paul", "enter"],
    ["Paul", "exit"],
    ["Paul", "exit"]
  ]),
  mismatches([
    ["Paul", "enter"],
    ["Paul", "exit"]
  ]),
  mismatches([
    ["Paul", "enter"],
    ["Paul", "enter"],
    ["Paul", "exit"],
    ["Paul", "exit"],
  ]),
  mismatches([
    ["Paul", "enter"],
    ["Paul", "exit"],
    ["Paul", "exit"],
    ["Paul", "enter"],
  ]));

Demo

Categories
interview

Implement getElementById() polyfill

Implement a method to search for an element in DOM by it’s Id. We use Breadth First Search (BFS) algorithm to traverse the DOM and store the elements in a queue in the following example.
Example: document.getElementById(‘demo’)

<div id="demo">
  <div>1</div>
  <div>2</div>
  <div>
    <div id="hello">Hello world!</div>
  </div>
  <div>3</div>
</div>
<script>
const getElementById = (element, id) => {
  const queue = [element];
  while (queue.length) {
    const curr = queue.shift();
    if (curr.id === id) {
      return curr;
    }
    if (curr.children.length) {
      queue.push(...curr.children);
    }
  }
};

console.log(getElementById(document.documentElement, 'hello').innerHTML); // Hello world!
</script>

Demo

Categories
interview

Valid Parentheses

/**
 * @param {string} str
 * @return {boolean}
 */
const isValid = function(str) {
  /*  
    if (typeof str !== 'string') {
      return false;
    }
  */
  const stack = [];
  const map = {
    ')': '(',
    '}': '{',
    ']': '['
  };

  for (const char of str) {
    if (Object.values(map).includes(char)) {
      stack.push(char);
    } else if (Object.keys(map).includes(char)) {
      if (map[char] !== stack[stack.length - 1]) {
        return false;
      }
      stack.pop();
    }
  }

  return stack.length === 0;
};

console.log(
  isValid('{{[test]})'), // false
  isValid('({[test]})'), // true
  isValid('({['), // false
  isValid(''), // true
);

/* You can verify the following cases as well, by uncommenting the if statement at the start of the method.
console.log(
  isValid(),            // false
  isValid(null),        // false
  isValid(0),           // false
  isValid(1),           // false
  isValid(1.12)         // false
);
*/

Demo

Categories
interview

HTML element as a datastore

The dataset read-only property of the HTMLElement interface provides read/write access to custom data attributes (data-*) on elements. It exposes a map of strings (DOMStringMap) with an entry for each data- attribute.

HTMLElement.dataset

<div id="demo" data-user="john"></div>
<script>
const element = document.getElementById('demo');

// Set a data attribute.
element.dataset.dateOfBirth = '2000-10-10';
// HTML: <div id="demo" data-user="john" data-date-of-birth="2000-10-10"></div>

delete element.dataset.dateOfBirth;
// HTML: <div id="demo" data-user="john"></div>
</script>

Demo

Categories
interview

React Component to render Directory Structure

import "./styles.css";

const Directory = (props) => {
  const { content } = props;

  return content.children.map((item) => {
    const isDirectory = "children" in item;
    return (
      <div class="container" key={item.name}>
        <span> {isDirectory ? "Dir - " : "File - "}</span>
        <span>{item.name}</span>
        {isDirectory && <Directory content={item} />}
      </div>
    );
  });
};

export default function App() {
  return (
    <Directory
      content={{
        name: "Root",
        children: [
          {
            name: "John"
          },
          {
            name: "Private",
            children: [
              {
                name: "Private1"
              },
              {
                name: "Private2"
              }
            ]
          }
        ]
      }}
    />
  );
}


/* Output

File - John
Dir - Private
  File - Private1
  File - Private2

*/
Categories
interview

Shallow copy vs Deep copy

// Shallow Copy.
const obj = {
  a: 1,
  b: 2,
  c: 3,
  d: {
    f: 4,
  },
};

const testObj = {
  ...obj // Similar to Object.assign()
};
testObj.d.f = 5; // This modified the original object.

console.log(obj.d);
// {
//    f: 4,
//  }

const newTestObj = {
  ...obj,
  d: {
    ...obj.d
  },
};

// Deep Copy.
const deepCopy = (inpObj) => {
  const obj = {};
  for (const [key, val] of Object.entries(inpObj)) {
    obj[key] = typeof val === 'object' ? deepCopy(val) : val;
  }
  return obj;
};

console.log(deepCopy(obj));
// {
//  a: 1,
//  b: 2,
//  c: 3,
//  d: {
//    f: 5
//   }
//  }

Demo

Categories
interview

Singleton pattern – Javascript

Often times we would want to limit number of instances of a class to 1. We can achieve this using the following pattern. You can use Object.freeze() to prevent further modifications of an object.

// Using ES6 Classes.
class Singleton {
  static getInstance() {
    if (!this.instance) {
      this.instance = new Object('Test');
    }
    return this.instance;
  }
}

console.log(Singleton.getInstance() === Singleton.getInstance());
  
// true

// Using functions.
function SingletonWrapper() {
  let instance;

  return {
    getInstance: function() {
      if (!instance) {
        instance = new Object('Test');
      }
      return instance;
    }
  }
}

const singletonWrapper = SingletonWrapper();

console.log(singletonWrapper.getInstance() === singletonWrapper.getInstance());
// true

Demo