JavaScript’s switch statement is one of the few things I find
      hard to remember the syntax for (so glad VS Code has autocomplete). It
      also feels a little bit out of place syntactically, as it’s the only thing
      that doesn’t use curly braces and you need to remember to
      break for every case. Moreover, its performance
      is less than stellar as its control flow is procedural.
    
      Luckily, JavaScript’s object literals are a pretty good alternative for
      most switch statement use-cases I can think of. The idea is
      to define an object with a key for each case you would have
      in a switch statement. Then you can access its value directly
      using the expression you would pass to the switch statement.
    
let fruit = 'oranges';
switch (fruit) {
  case 'apples':
    console.log('Apples');
    break;
  case 'oranges':
    console.log('Oranges');
    break;
}
// Logs: 'Oranges'
const logFruit = {
  'apples': () => console.log('Apples'),
  'oranges': () => console.log('Oranges')
};
logFruit[fruit](); // Logs: 'Oranges'
    
      While this is infinitely more readable and less verbose, it’s also
      significantly faster. However, we haven’t yet addressed the elephant in
      the room: the default case. To handle it, we can just add a
      'default' key and check if the expression’s value exists in
      our object.
    
let fruit = 'strawberries';
switch (fruit) {
  case 'apples':
    console.log('Apples');
    break;
  case 'oranges':
    console.log('Oranges');
    break;
  default:
    console.log('Unknown fruit');
}
// Logs: 'Unknown fruit'
const logFruit = {
  'apples': () => console.log('Apples'),
  'oranges': () => console.log('Oranges'),
  'default': () => console.log('Unknown fruit')
};
(logFruit[fruit] || logFruit['default'])(); // Logs: 'Unknown fruit'
    
      Finally, our object literal replacement should be able to handle falling
      through cases, similar to what happens when there’s no
      break statement. This is a matter of simply extracting and
      reusing logic in the object literal.
    
let fruit = 'oranges';
switch (fruit) {
  case 'apples':
  case 'oranges':
    console.log('Known fruit');
    break;
  default:
    console.log('Unknown fruit');
}
// Logs: 'Known fruit'
const knownFruit = () => console.log('Known fruit');
const unknownFruit = () => console.log('Unknown fruit');
const logFruit = {
  'apples': knownFruit,
  'oranges': knownFruit,
  'default': unknownFruit
};
(logFruit[fruit] || logFruit['default'])(); // Logs: 'Known fruit'
    
      To wrap this all up, we can generalize and extract this logic into a
      simple reusable function. We will supply it with the lookup object and an
      optional name for the default case (we’ll default to
      _default to avoid any conflicts). This function will in turn
      return a function with the appropriate lookup logic and we can use it to
      replace any switch statement.
    
const switchFn = (lookupObject, defaultCase = '_default') =>
  expression => (lookupObject[expression] || lookupObject[defaultCase])();
const knownFruit = () => console.log('Known fruit');
const unknownFruit = () => console.log('Unknown fruit');
const logFruit = {
  'apples': knownFruit,
  'oranges': knownFruit,
  'default': unknownFruit
};
const fruitSwitch = switchFn(logFruit, 'default');
fruitSwitch('apples'); // Logs: 'Known fruit'
fruitSwitch('pineapples'); // Logs: 'Unknown fruit'