Fire tips by tag

Fire tips tagged “JavaScript

Catching unknown keys in lookup tables

The lookup table pattern can replace some long if-else branches and switch statements. We can catch unknown keys we don’t have values for with hasOwnProperty.

const getColorOf = thing => {
  const map = {
    clouds: 'white',
    grass: 'green',
    sky: 'blue',
  }
  
  // throw an error if `thing` isn’t a key in the map
  if (!map.hasOwnProperty(thing)) {
    throw new Error(`Color not defined for: ${thing}`)
  }

  // if we get here, `thing` IS a key in the map
  return map[thing]
}

getColorOf('grass')  // ⇒ 'green'
getColorOf('air')    // ⇒ Color not defined for: air
getColorOf()         // ⇒ Color not defined for: undefined

Accessing array elements faster with lookup objects

If you identify objects in an array by a unique property, turn the array into a lookup object. The unique property becomes a key, giving you access to the objects without having to search for them in the array first.

// this array holds objects that all have name- and type-properties
const array = [
  { name: 'Bulbasaur',  type: 'Grass/Poison' },
  { name: 'Charmander', type: 'Fire'         },
  { name: 'Squirtle',   type: 'Water'        },
  { name: 'Pikachu',    type: 'Electric'     }
]

// to get the type for a name, we need to .find() the element first
array.find(pokemon => pokemon.name === 'Charmander').type
// ⇒ 'Fire'


// this lookup object uses the unique names as keys
const object = {
  'Bulbasaur':  { name: 'Bulbasaur',  type: 'Grass/Poison' },
  'Charmander': { name: 'Charmander', type: 'Fire'         },
  'Squirtle':   { name: 'Squirtle',   type: 'Water'        },
  'Pikachu':    { name: 'Pikachu',    type: 'Electric'     }
}

// lookups work with bracket notation, which is faster than .find()
object['Charmander'].type
// ⇒ 'Fire'

Splitting arrays into X elements and “the rest”

This little helper can split arrays into the first X elements and “the rest”. You could use it to show the first five photos from a list and a link that says “27 more photos”.

// takes an array and the number of elements to extract from the start
const spliceHead = (array, sizeOfHead) => {
  // create a copy, because `.splice()` changes the array in place
  const copy = [...array]

  // cut the first `sizeOfHead` elements from the array and store them
  const head = copy.splice(0, sizeOfHead)

  // return TWO arrays: the first `sizeOfHead` elements and “the rest”
  return [head, copy]
}

// this can split an array into “the first three, and everything else”
spliceHead(['John', 'Paul', 'George', 'Ringo'], 3)
// ⇒ [ ['John', 'Paul', 'George'], ['Ringo'] ]

// we can assign the results to two variables with destructuring
const [head, rest] = spliceHead(['Bibidi', 'Babidi', 'Boo'], 2)
console.log(`${head[0]}, ${head[1]}, and ${rest.length} more`)
// ⇒ 'Bibidi, Babidi, and 1 more'

Math with Infinity

Calculations with Infinity are pretty rare, and most of them still return Infinity. While Infinity is a number, trying to subtract it from another Infinity isn’t a number anymore.

// both `Infinity` and numbers are numbers
typeof Infinity      // ⇒ "number"
typeof 1000          // ⇒ "number"

// regular calculations with `Infinity` still return `Infinity`
Infinity + 1000      // ⇒ Infinity
Infinity - 2000      // ⇒ Infinity
Infinity * 3000      // ⇒ Infinity
Infinity / 4000      // ⇒ Infinity

// some of these return `NaN` when we repeat the calculation with `Infinity`
Infinity + Infinity  // ⇒ Infinity
Infinity - Infinity  // ⇒ NaN
Infinity * Infinity  // ⇒ Infinity
Infinity / Infinity  // ⇒ NaN

Relative timestamps with Intl

JavaScript has a built-in formatter for relative timestamps. It returns strings like “yesterday” or “2 months from now” in the language you specify.

This does not currently work in IE and Safari.

// a positive value describes a time in the future
new Intl.RelativeTimeFormat('en').format(2, 'day')
// ⇒ 'in 2 days'

// a negative value describes a time in the past
new Intl.RelativeTimeFormat('en').format(-1, 'day')
// ⇒ '1 day ago'

// `numeric: 'auto'` results in strings like “yesterday” and “tomorrow”
new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(-1, 'day')
// ⇒ 'yesterday'

// the formatter can base its output on different languages
new Intl.RelativeTimeFormat('de').format(3, 'month')
// ⇒ 'in 3 Monaten'

Extracting floats from strings

To get all float values from a sentence, we can first find them with a regular expression and then map over the matches with parseFloat.

// matches all integer or decimal substrings, like “69” or “4.20”
const numbersRegexp = new RegExp(/\d+(\.\d+)?/, 'g')

// finds all numbers matching the expression and transforms them to numbers
const getFloatsFrom = string => string.match(numbersRegexp).map(parseFloat)

getFloatsFrom("Put the 16 cupcakes in the oven (at 180°C) for 1.5 hours.")
// ⇒ [16, 180, 1.5]

Mapping array values with constructors

Some type constructors can be called as regular functions in JavaScript. We can use them when mapping over arrays to quickly transform the values in them to that type.

// values passed to a constructor are transformed to that type
[4, 8, 15].map(number => String(number))          // ⇒ ["4", "8", "15"]
["16", "23", "42"].map(string => Number(string))  // ⇒ [16, 23, 42]
[1, 1, 0].map(value => Boolean(value))            // ⇒ [true, true, false]

// we only pass the values through, so we can use this shorthand notation
[4, 8, 15].map(String)          // ⇒ ["4", "8", "15"]
["16", "23", "42"].map(Number)  // ⇒ [16, 23, 42]
[1, 1, 0].map(Boolean)          // ⇒ [true, true, false]

Removing duplication with ternary operators

One use of the ternary operator is to reduce duplication. If we’d call the same function with a different parameter in each case, we can move the ternary into the function call.

Watch out for readability: the shortest version isn’t always the best option.

// we want to wish people a happy weekend/workweek depending on the day
const isWeekend = day === 'Saturday' || day === 'Sunday'

// we can call `console.log` with the respective message like this
isWeekend ? console.log('Happy weekend!') : console.log('Happy workweek!')

// `console.log` happens in any case, so we can move the ternary inside it
console.log(isWeekend ? 'Happy weekend!' : 'Happy workweek!')

// we can extract the duplicate “Happy ” and “!” to only write them once
console.log(`Happy ${isWeekend ? 'weekend' : 'workweek'}!`)

// don’t go crazy and extract EVERY duplication; the code becomes shorter
// if we extract the duplicate “w”, but also much harder to read
console.log(`Happy w${isWeekend ? 'eekend' : 'orkweek'}!`)

Replacing all matches in a string

When replacing a substring in a string, only the first match is replaced. If we want to replace all matches, we need to use a regular expression and tell it to match globally within the string.

// if the first parameter is a string, only the first match is replaced
'Pika pika!'.replace('ka', 'dgey')   // ⇒ 'Pidgey pika!'

// we can write the pattern as a regular expression, which achieves the same
'Pika pika!'.replace(/ka/, 'dgey')   // ⇒ 'Pidgey pika!'

// with the additional `g`-flag, all matches (globally) are replaced
'Pika pika!'.replace(/ka/g, 'dgey')  // ⇒ 'Pidgey pidgey!'

Making parameters required

Functions in JavaScript don’t have “required” parameters. We can emulate them by setting a function that throws an error as a parameter’s default value. This assigned function call only happens if we omit the parameter, making it required.

// calling this function always throws an error
const throwIfMissing = parameter => {
  throw new Error(`Parameter '${parameter}' needs to be defined.`)
}

// if we don’t pass a `thing`, that function will be called as the default
const askAboutFavorite = (thing = throwIfMissing('thing')) => {
  console.log(`What is your favorite ${thing}?`)
}

// not passing anything, or passing `undefined`, causes the error to happen
askAboutFavorite()           // Error: "Parameter 'thing' needs to be defined."
askAboutFavorite(undefined)  // Error: "Parameter 'thing' needs to be defined."

// passing any defined value is fine
askAboutFavorite(null)       // "What is your favorite null?"
askAboutFavorite('color')    // "What is your favorite color?"

Assignment operators shorthand

JavaScript has many different assignment operators that are shorthand for longer versions. += is one of the more common ones. Most other calculations also have an assignment operator.

// regular assignment assigns the right value to the variable on the left
a = b

// we can shorten the code if the variable is itself part of the calculation
a = a + 5    // is the same as:
a += 5

// these are some of the available shorthand assignment operators
a += b    // a = a + b       addition assignment
a -= b    // a = a - b       subtraction assignment
a *= b    // a = a * b       multiplication assignment
a /= b    // a = a / b       division assignment
a %= b    // a = a % b       remainder assignment

a &&= b   // a = a && b      logical AND assignment
a ||= b   // a = a || b      logical OR assignment

Removing the largest number from an array

We can remove all instances of the largest number in an array by combining Math.max() and Array.prototype.filter().

// takes an array of numbers and removes all the maximum values
const removeMax = numbers => {
  // get this before we `filter` so we only need to calculate it once
  const max = Math.max(...numbers)

  // return a version of `numbers` with all instances of `max` removed
  return numbers.filter(number => number !== max)
}

// if the maximum value appears multiple times, all of them are removed
removeMax([12, 27, 8, 9, 41, 33, 41, 29])  // ⇒ [12, 27, 8, 9, 33, 29]

// if all numbers are the maximum, the function returns an empty array
removeMax([5, 5, 5, 5, 5, 5, 5])           // ⇒ []

// this works with negative numbers as well
removeMax([-5, -2, -8, -1, -10])           // ⇒ [-5, -2, -8, -10]

Sorting arrays by object properties

By passing a function when sorting an array, we can sort objects by different properties.

const people = [
  { name: 'Lisa',   age: 8  },
  { name: 'Maggie', age: 1  },
  { name: 'Homer',  age: 34 },
  { name: 'Bart',   age: 10 },
  { name: 'Marge',  age: 33 }
]

// sort `people` by name (alphabetically)
people.sort((a, b) => a.name > b.name)
// ⇒ [
//     { name: 'Bart',   age: 10 },
//     { name: 'Homer',  age: 34 },
//     { name: 'Lisa',   age: 8  },
//     { name: 'Maggie', age: 1  },
//     { name: 'Marge',  age: 33 }
//   ]

// sort `people` by age (from oldest to youngest)
people.sort((a, b) => b.age - a.age)
// ⇒ [
//     { name: 'Homer',  age: 34 },
//     { name: 'Marge',  age: 33 },
//     { name: 'Bart',   age: 10 },
//     { name: 'Lisa',   age: 8  },
//     { name: 'Maggie', age: 1  }
//   ]

// sort `people` by name (from longest to shortest)
people.sort((a, b) => a.name.length - b.name.length)
// ⇒ [
//     { name: 'Bart',   age: 10 },
//     { name: 'Lisa',   age: 8  },
//     { name: 'Homer',  age: 34 },
//     { name: 'Marge',  age: 33 },
//     { name: 'Maggie', age: 1  }
//   ]

Short-circuiting Boolean expressions

In JavaScript, && and || “short circuit”. If the result is obvious from the left value, it doesn’t evaluate the right side because that value wouldn’t make a difference.

// This function takes a long time. We don’t want to call it unnecessarily.
const someReallyComplicatedFunction = () => {
  // something that returns a Boolean value
}

// The result of `true && SOMETHING` depends on the value of SOMETHING.
// JavaScript has to call the function here.
true && someReallyComplicatedFunction()   // function is run

// The result of `false && SOMETHING` can never be `true`. JavaScript skips
// the right side, because that value doesn’t matter.
false && someReallyComplicatedFunction()  // function is skipped

// `||` stops as soon as one value is `true`. If the first value is already
// true, it skips the right side.
true || someReallyComplicatedFunction()   // function is skipped

// If the value left of `||` is `false`, we need to check the right value.
// JavaScript has to call the function here.
false || someReallyComplicatedFunction()  // function is run

Quickly removing values from an array

Setting the length of an array in JavaScript removes all values beyond that length. Setting the length to zero removes all values from it.

// this array holds six values
const numbers = [4, 8, 15, 16, 23, 42]

// by setting the length to 4, any value past the fourth element is removed
numbers.length = 4
console.log(numbers)  // ⇒ [4, 8, 15, 16]

// setting the length to 0 leads to the entire array being emptied
numbers.length = 0
console.log(numbers)  // ⇒ []

Freezing objects for immutability

In JavaScript, const doesn’t mean a value is constant and can never be changed. To make an object immutable, we have to freeze it.

// the word `const` makes it sound like this object can never be modified
const dog = { name: 'Coop' }
// dog → { name: 'Coop' }

// we can still change properties of an object defined with `const`
dog.name = 'Cooper'
// dog → { name: 'Cooper' }

// we can also add new properties to it
dog.age = 3
// dog → { name: 'Cooper', age: 3 }

// the only thing we cannot do is assign an entirely different object
dog = { name: 'Emil' }  // TypeError (cannot assign to readonly property)

// to prevent ALL changes to an object, we have to freeze it
const cat = Object.freeze({ name: 'Milly' })
// cat → { name: 'Milly' }

// frozen objects can’t be changed (these steps throw errors in strict mode)
cat.name = 'Lilly'
cat.age = 9
// cat → { name: 'Milly' }

Padding strings

When we want a short string to be a certain number of characters long, we can “pad” it with other characters on the left or right side. Spaces are common for this, but we can pad with any character.

// we can pad a string from the front with .padStart()
    '1'.padStart(5, ' ')     // ⇒ '    1'
   '12'.padStart(5, ' ')     // ⇒ '   12'
  '123'.padStart(5, ' ')     // ⇒ '  123'
 '1234'.padStart(5, ' ')     // ⇒ ' 1234'
'12345'.padStart(5, ' ')     // ⇒ '12345'

// we can pad from the end with .padEnd()
    '1'.padEnd(5, ' ')       // ⇒ '1    '
   '12'.padEnd(5, ' ')       // ⇒ '12   '
  '123'.padEnd(5, ' ')       // ⇒ '123  '
 '1234'.padEnd(5, ' ')       // ⇒ '1234 '
'12345'.padEnd(5, ' ')       // ⇒ '12345'

// strings that are longer than the minimum don’t get padded
'123456'.padStart(5, ' ')    // ⇒ '123456'

// we can use other characters to pad the string with
'6310'.padStart(16, '*')     // ⇒ '************6310'

// the string to pad with is truncated when the expected length is reached
'hello'.padEnd(9, ' world')  // ⇒ 'hello wor'

Clamping values in an array

To make sure all numbers in an array are within a given range, we can “clamp” them. Values below the minimum become min, values above the maximum become max.

// takes a minimum and maximum value, returns a function that clamps a value
const clamp = (min, max) => value => Math.max(Math.min(value, max), min)

// we can call this function on individual values
clamp(2, 3)(1)  // ⇒ 2 (because 1 is smaller than the minimum value of 2)
clamp(2, 3)(2)  // ⇒ 2
clamp(2, 3)(3)  // ⇒ 3
clamp(2, 3)(4)  // ⇒ 3 (because 4 is greater than the maximum value of 3)

// because the function is curried, we can pass it to .map() like this
[1, 16, 9, 0, -2, 8, 14].map(clamp(0, 10))  // ⇒ [1, 10, 9, 0, 0, 8, 10]

Building objects from nested arrays

If you have key-value-pairs in nested arrays, you can turn them into an object with Object.fromEntries().

const pairs = [
  ['name', 'Bob'],
  ['occupation', 'Fry cook'],
  ['shape', 'rectangular']
]

Object.fromEntries(pairs)
// ⇒ {
//     name: 'Bob',
//     occupation: 'Fry cook',
//     shape: 'rectangular'
//   }

Russian roulette

Add thrill and excitement to your JavaScript with some Russian roulette. This helper calls a function you give it with a chance of 5 in 6. In the other case, it causes an infinite loop and crashes everything.

// takes a function and either calls it or crashes absolutely everything
const russianRoulette = fn => {
  // pick a random number from 0 to 5
  const randomNumber = Math.floor(Math.random() * 6)

  // crash the code with an infinite loop if the random number was 5
  while (randomNumber === 5) {}
  
  // call the function and return its result if the number wasn’t 5
  return fn()
}

// This logs the string with a chance of 5 in 6 (~83.3%). In the other case,
// everything crashes.
russianRoulette(() => console.log('You are lucky!'))

Getting the array element with the largest given property

We can get the largest number from an array of numbers with Math.max(), but that does not work with objects. If we have an array of objects, we can get the element with the largest value of a given property like this:

// takes an array and the name of a property to compare by
const maxBy = (array, prop) => {
  return array.reduce((max, element) => element[prop] > max[prop] ? element : max)
}

// find the object with the largest `age`
maxBy([
  { name: 'Leela',  age: 31   },
  { name: 'Fry',    age: 1031 },
  { name: 'Hubert', age: 165  },
  { name: 'Bender', age: 10   }
], 'age')
// ⇒ { name: 'Fry', age: 1031 }

Removing specific values from an array

Going through Set is cool to filter out duplicates. If you need to filter out specific values instead, you can use this helper.

// takes an array of values and a second array of values to take out of it
const without = (array, valuesToRemove) => {
  return array.filter(value => !valuesToRemove.includes(value))
}

// all instances of the values to filter out will be removed
without([0, 8, 12, 15, 4, 8, 15, 23], [8, 15])        // ⇒ [0, 12, 4, 23]

// using `.split()` and `.join()`, we can quickly filter values from strings
without('↑↑↓↓←→←→BA'.split(''), ['←', '↑']).join('')  // ⇒ '↓↓→→BA'

Counting all array elements that match a condition

Want to know how many elements in an array match a condition? We can use this to count all elements in an array that return true for a function that checks this condition.

// takes an array and a function to check each element in the array against
const countBy = (array, checkFunction) => {
  return array.reduce((count, element) => {
    return checkFunction(element) ? count + 1 : count
  }, 0)
}

// we can count how many numbers in an array are smaller than a given limit
countBy([-4, 3, -7, 5, 0, -3, 1, -8], number => number < 0)
// ⇒ 4

// we can count how many strings are exactly four characters long
countBy(['John', 'Paul', 'George', 'Ringo'], name => name.length === 4)
// ⇒ 2

// we can also check if objects have a certain value in a property
countBy([
  {
    name: 'Bulbasaur',
    types: ['Grass', 'Poison']
  }, {
    name: 'Nidoqueen',
    types: ['Poison', 'Ground']
  }, {
    name: 'Abra',
    types: ['Psychic']
  }, {
    name: 'Tentacool',
    types: ['Water', 'Poison']
  }
], pokemon => pokemon.types.includes('Poison'))
// ⇒ 3

Composing functions to run in sequence

If you need to nest functions, or store their output only to pass it to another function, you can instead compose a function that runs an input through many functions in sequence.

// these functions all take a string and do something with it
const reverse = name => [...name].reverse().join('')
const upcase = name => name.toUpperCase()
const greet = name => `Hi, ${name}!`

// we could store each result and pass it to the next function
const reversed = reverse('john')
const upcased = upcase(reversed)
greet(upcased)
// ⇒ 'Hi, NHOJ!'

// if we nest them like this instead, they are run from inside to outside
greet(upcase(reverse('john')))
// ⇒ 'Hi, NHOJ!'

// this takes several functions and runs an input through them in sequence
const compose = (...fns) => input => {
  return fns.reduce((result, fn) => fn(result), input)
}

// with our `compose`, we can list the functions and call them like this
compose(reverse, upcase, greet)('john')
// ⇒ 'Hi, NHOJ!'

// the order of functions defines the order the steps are executed in
compose(greet, reverse, upcase)('john')
// ⇒ '!NHOJ ,IH'

// we can also assign our composed function to a variable and reuse it
const sequence = compose(upcase, greet, reverse)
sequence('john')  // ⇒ '!NHOJ ,iH'
sequence('paul')  // ⇒ '!LUAP ,iH'

Mapping over objects

We can map over an array, but we cannot natively map over an object. This helper function allows us to apply the same function to all properties of an object.

// we’ll use this to try and double a bunch of values in this example
const double = number => number * 2

// we put three numbers into an array, and also into an object as properties
const numbers = [1, 2, 3]
const coordinates = { x: 1, y: 2, z: 3 }

// we can `map` over an array to double all elements
numbers.map(double)
// ⇒ [2, 4, 6]

// we cannot `map` an object, because objects don’t have a map-function
coordinates.map(double)  // causes a TypeError


// this takes an object and a function to apply to _every_ property in it
const mapObject = (object, theFunction) => {
  // get all of the object’s keys and use `theFunction` on their values
  return Object.keys(object).reduce((result, key) => ({
    ...result,
    [key]: theFunction(object[key])
  }), {})
}

// `mapObject` can map all properties for us
mapObject(coordinates, double)
// ⇒ { x: 2, y: 4, z: 6 }

Flat arrays to object arrays

If an array contains flat values, we can quickly turn those into objects by mapping over them. The name we give them in the call to Array.prototype.map() becomes the name of the objects’ property when we do it like this.

// turn these strings into objects, where they become `name`-properties
['John', 'Paul', 'George', 'Ringo'].map(name => ({ name }))
// ⇒ [
//     { name: 'John'   },
//     { name: 'Paul'   },
//     { name: 'George' },
//     { name: 'Ringo'  }
//   ]

Spongebobifying strings

Want to use JavaScript to make Spongebob memes? This function transforms a StRiNg LiKe ThIs. It turns the first letter into uppercase, and then alternates between lowercase and uppercase for all following letters.

const spongebobify = string => {
  return string.split('').reduce(({ result, isUppercase }, char) => {
    // check if the character is a letter from a to z (upper- or lowercase)
    if (/[a-zA-Z]/.test(char)) {
      // change the casing of the letter
      const letter = isUppercase ? char.toUpperCase() : char.toLowerCase()

      // append updated letter and flip `isUppercase` for next iteration
      return {
        result: `${result}${letter}`,
        isUppercase: !isUppercase
      }
    }
    
    // append unchanged non-letter (like numbers), keep `isUppercase` as is
    return {
      result: `${result}${char}`,
      isUppercase
    }
  }, {
    // these are the default values we go into the reduce-function with
    result: '',
    isUppercase: true
  }).result
}

// this has very little practical use
spongebobify('string like this')     // ⇒ StRiNg LiKe ThIs
spongebobify('This is a seaplane.')  // ⇒ ThIs Is A sEaPlAnE.

Conditionally modifying array elements

If we want to change only the values in an array that match a condition, we can combine Array.prototype.map() with a ternary operator. By putting these in a function, we can quickly apply this pattern to many different scenarios.

// takes an array, a function that checks each element, and another function
// that it applies to every element that the check returns `true` for
const mapMatches = (array, checkFunction, changeFunction) =>
  array.map(element => checkFunction(element) ? changeFunction(element) : element)

// change every name that contains an “e” into uppercase
mapMatches(
  ['Leo', 'Paul', 'Joe', 'Fred', 'Sam'],
  name => name.includes('e'),
  name => name.toUpperCase()
)
// ⇒ ['LEO', 'Paul', 'JOE', 'FRED', 'Sam'],

// divide every even number by two
mapMatches(
  [4, 8, 15, 16, 23, 42],
  number => number % 2 === 0,
  number => number / 2
)
// ⇒ [2, 4, 15, 8, 23, 21]

The optional-chaining operator

We can get nested values from objects by chaining their keys. We run into an error when we try to get a value from something that does not exist. With optional chaining, these calls return undefined instead of throwing errors.

// this person’s address is nested in the outer object as another object
const mario = {
  occupation: 'Plumber',
  address: {
    street: 'Rainbow Road',
    zip: '81664'
  }
}

// we can get the full address, or drill into it to get the street
mario.address         // ⇒ { street: 'Rainbow Road': zip: '81664' }
mario.address.street  // ⇒ 'Rainbow Road'

// we don’t have a nested address for this person
const toad = {
  occupation: 'Explorer'
}

// if we try to get the street, JavaScript will throw an error
toad.address         // ⇒ undefined
toad.address.street  // TypeError: undefined is not an object

// the ?. operator returns `undefined` if it hits any undefined value
toad?.address          // ⇒ undefined (same as without the questionmark)
toad?.address?.street  // ⇒ undefined (no error!)

Grouping array elements by a property

If objects in an array are categorized by a property, we can group them by that property like this. We can then quickly get all objects with a certain value instead of having to filter all the time.

// takes an array of objects and the name of a property of these objects
const groupBy = (array, property) => array.reduce((grouped, element) => ({
  ...grouped,
  [element[property]]: [...(grouped[element[property]] || []), element]
}), {})

// some elements in this array share the same value for their `type`
const team = [
  { name: 'Squirtle', type: 'Water'    },
  { name: 'Pikachu',  type: 'Electric' },
  { name: 'Arcanine', type: 'Fire'     },
  { name: 'Psyduck',  type: 'Water'    },
  { name: 'Vulpix',   type: 'Fire'     }
]

// `groupBy` groups an array into a dictionary based on the given property
const groupedByType = groupBy(team, 'type')
// ⇒ {
//     'Water': [
//       { name: 'Squirtle', type: 'Water' },
//       { name: 'Psyduck',  type: 'Water' }
//     ],
//     'Electric': [
//       { name: 'Pikachu',  type: 'Electric' }
//     ],
//     'Fire': [
//       { name: 'Arcanine', type: 'Fire' },
//       { name: 'Vulpix',   type: 'Fire' }
//     ]
//   }

// we can get a list of all values from the dictionary’s keys
Object.keys(groupedByType)
// ⇒ ['Water', 'Electric', 'Fire']

// we can then extract only the elements that share the same type
groupedByType['Fire']
// ⇒ [
//     { name: 'Arcanine', type: 'Fire' },
//     { name: 'Vulpix',   type: 'Fire' }
//   ]

Exclusive OR

Finding values that exist in only one of two arrays is called “XOR”, or “exclusive OR”. It gets us all values that appear in either the first OR the second array, but not both.

// takes two arrays and returns all elements that appear in only one of them
const xor = (first, second) => [
  ...first.filter(element => !second.includes(element)),
  ...second.filter(element => !first.includes(element))
]

// `xor` returns all elements that appear in only one of the two arrays
xor([1, 2, 3, 4, 5], [2, 3, 4, 5, 6])   // ⇒ [1, 6]

// an element can appear multiple times, but only in one array
xor(['beep', 'beep', 'boo', 'boo'], ['beep'])  // ⇒ ['boo', 'boo']

Filtering arrays to unique values

Arrays in JavaScript can contain the same value several times. Going through Set, we can filter them down to unique values.

const unique = array => [...new Set(array)]

// each value in this array is also how many of it are in there
unique([1, 2, 3, 3, 4, 4, 3, 4, 2, 4])  // ⇒ [1, 2, 3, 4]

Getting the last element from an array

Getting the last element in an array by doing math with its length can lead to off-by-one errors. Doing this with Array.prototype.pop() would remove the value from the array. We can write a function that leaves the value in the array and does the math with length for us.

const last = array => array[array.length - 1]

last(['Bibidi', 'Babidi', 'Boo'])  // ⇒ 'Boo'
last([59, 75, 78, 752, 789, 881])  // ⇒ 881

The exponentiation operator

Math.pow() raises a base to an exponent. The recently introduced “exponentiation operator” ** does the same but shorter.

// we can use `Math.pow(x, y)` to calculate x^y
Math.pow(2, 2)     // ⇒  2 ^ 2   =  4
Math.pow(4, 3)     // ⇒  3 ^ 3   = 64
Math.pow(25, 0.5)  // ⇒ 25 ^ 0.5 =  5

// `x ** y` does the same, but shorter
2 ** 2             // ⇒  2 ^ 2   =  4
4 ** 3             // ⇒  4 ^ 3   = 64
25 ** 0.5          // ⇒ 25 ^ 0.5 =  5

Merging arrays

To merge arrays in JavaScript, we can either concatenate them or use the spread operator. If we nest them in an array, they stay separate inside of that wrapping array instead.

const a = ['up', 'down']
const b = ['left', 'right']
const c = ['B', 'A']

// this nests the arrays in another array instead of merging them
[a, b, c]           // ⇒ [['up', 'down'], ['left', 'right'], ['B', 'A']]

// we can concatenate many arrays at once with `.concat()`
a.concat(b, c)      // ⇒ ['up', 'down', 'left', 'right', 'B', 'A']

// the spread operator also joins the arrays into one large array
[...a, ...b, ...c]  // ⇒ ['up', 'down', 'left', 'right', 'B', 'A']

Flattening nested arrays

Array.prototype.flat() can un-nest nested arrays. It can remove only some levels, or all of them if we tell it to go infinitely many levels deep.

// each value in this array is also how many levels deep it is nested
const nested = [ 0, [1], [[2]], [[[3]]], [[[[[[[[[9]]]]]]]]] ]

// the parameter given to `flat` is how many levels of nesting it removes
nested.flat(0)         // ⇒ [ 0, [1], [[2]], [[[3]]], [[[[[[[[[9]]]]]]]]] ]
nested.flat(1)         // ⇒ [ 0,  1,   [2],   [[3]],   [[[[[[[[9]]]]]]]]  ]
nested.flat(2)         // ⇒ [ 0,  1,    2,     [3],     [[[[[[[9]]]]]]]   ]
nested.flat(Infinity)  // ⇒ [ 0,  1,    2,      3,             9          ]

Shuffling arrays

The Fisher-Yates shuffle is a fast, unbiased way to shuffle arrays. It works by starting from the back, swapping the last element with one before it. This is repeated while moving towards the front until every element has been swapped at least once.

const shuffle = array => {
  // create a clone of the array that we can rearrange in place
  const copy = array.slice()

  // start at the last element, moving towards the front with every repetition
  for (let i = copy.length - 1; i > 0; i--) {
    // get the index of an element left of (and including) the current one
    const j = Math.floor(Math.random() * (i + 1))

    // swap the values at the positions i and j
    const temp = copy[i]
    copy[i] = copy[j]
    copy[j] = temp
  }

  return copy
}

shuffle([1, 2, 3, 4])  // ⇒ [4, 1, 2, 3] (maybe)
shuffle([1, 2, 3, 4])  // ⇒ [3, 1, 2, 4] (maybe)
shuffle([1, 2, 3, 4])  // ⇒ [1, 4, 2, 3] (maybe)
shuffle([1, 2, 3, 4])  // ⇒ [3, 2, 1, 4] (maybe)

Inverting Boolean functions

We can invert Boolean values with an exclamation mark. That doesn’t work for function names we use as shorthand in array methods like Array.prototype.filter() and Array.prototype.map(). Wrap those in a helper function to have them return the opposite of what they would return normally.

const numbers = [0, 1, 2, 3, 4, 5]
const isEven = n => n % 2 === 0

// the long and short form of this do the same
numbers.filter(number => isEven(number))  // ⇒ [0, 2, 4]
numbers.filter(isEven)                    // ⇒ [0, 2, 4]

// `!` can flip the Boolean value, but it only works with the long form
numbers.filter(number => !isEven(number))  // ⇒ [1, 3, 5]
numbers.filter(!isEven)                    // TypeError (not a function)

// this (curried) helper makes functions return a flipped result
const not = callback => value => !callback(value)

// we can use `not` like this, in both the long and short form
numbers.filter(number => not(isEven)(number))  // ⇒ [1, 3, 5]
numbers.filter(not(isEven))                    // ⇒ [1, 3, 5]

Splitting strings by lengths

JavaScript can split strings at given characters. By matching against a regular expression, we can split strings into substrings of a given length instead.

// split at every `O`; all `O`s are gone in the result
'BRBIDKLOLOMGOK'.split('O')  // ⇒ ['BRBIDKL', 'L', 'MG', 'K']

// split into groups of up to 3 characters (the last group can be shorter)
'BRBIDKLOLOMGOK'.match(/.{1,3}/g)  // ⇒ ['BRB', 'IDK', 'LOL', 'OMG', 'OK']

Destructuring values returned by functions

If a function returns an object, we can destructure the result afterwards. We can take out only the values we need that way.

// this function does some math and returns multiple results
const doSomeMath = array => {
  const sum = array.reduce((sum, value) => sum + value, 0)

  // we return an object with three values instead of just a single value
  return {
    min: Math.min(...array),
    max: Math.max(...array),
    average: sum / array.length
  }
}

// we can pick the values we want; we can skip `min` if we don’t need it
const { max, average } = doSomeMath([15, 4, 23, 16, 42, 8])
// max:     42
// average: 18

Filtering falsy values from arrays

The constructor for Boolean values is all you need to filter all falsy values from an array. Keep in mind that this takes out false, 0, null, undefined, empty strings, and NaN. If you don’t want that, filter by something more specific.

const values = [
  5, null, false, 'hi', 0, undefined, { name: 'Tim' }, '', true, NaN, [7]
]

// `Boolean` takes out ALL falsy values
values.filter(Boolean)
// ⇒ [5, 'hi', { name: 'Tim' }, true, [7]]

// `x != null` leaves most falsy values, taking out `null` and `undefined`
values.filter(value => value != null)
// ⇒ [5, false, 'hi', 0, { name: 'Tim' }, '', true, NaN, [7]]

Combining arrays pairwise

Need to combine values from two arrays pairwise? This function takes the two first values, the two second values, and so on and puts them in nested arrays. The operation is called “zip” because it works like a zipper.

// takes two arrays and combines their elements pairwise
const zip = (firstArray, secondArray) => {
  const longerLength = Math.max(firstArray.length, secondArray.length)
  const indices = [...new Array(longerLength).keys()]

  return indices.reduce((list, index) => [
    ...list,
    [firstArray[index], secondArray[index]]
  ], [])
}

zip([4, '8 oz', '2 cups', 'some'], ['eggs', 'milk', 'flour', 'salt'])
// ⇒ [[4, 'eggs'], ['8 oz', 'milk'], ['2 cups', 'flour'], ['some', 'salt']]

// if one array is shorter, that list’s value in a pair is `undefined`
zip([1, 2, 3], ['a', 'b'])       // ⇒ [[1, 'a'], [2, 'b'], [3, undefined]]
zip([1, 2],    ['a', 'b', 'c'])  // ⇒ [[1, 'a'], [2, 'b'], [undefined, 'c']]

Plucking properties from object arrays

One of the more common uses for Array.prototype.map() is extracting properties from objects. Instead of using individual arrow functions, we can create a reusable function that does the plucking for us.

const countries = [
  { name: 'France', capital: 'Paris'  },
  { name: 'Spain',  capital: 'Madrid' },
  { name: 'Italy',  capital: 'Rome'   }
]

// we can extract the attributes with individual arrow functions
countries.map(country => country.name)     // ⇒ ['France', 'Spain', 'Italy']
countries.map(country => country.capital)  // ⇒ ['Paris', 'Madrid', 'Rome']

// this function allows us to write that arrow function shorter
const pluck = property => element => element[property]

countries.map(pluck('name'))     // ⇒ ['France', 'Spain', 'Italy']
countries.map(pluck('capital'))  // ⇒ ['Paris', 'Madrid', 'Rome']

Getting random elements from arrays

Getting a random number is fun, but it’s not “getting a random element from an array” fun. I don’t know why you’d want to do this, but you can.

// get a random element from the given array
const sample = array => array[Math.floor(Math.random() * array.length)]

// every time we use `sample`, it returns a random element
sample([17, 6, 22, 13, 9])  // ⇒ 13 (maybe)
sample([17, 6, 22, 13, 9])  // ⇒ 6  (maybe)
sample([17, 6, 22, 13, 9])  // ⇒ 6  (maybe)
sample([17, 6, 22, 13, 9])  // ⇒ 17 (maybe)

Splitting arrays by a condition

We can split arrays in two based on a condition. Everything that matches the condition goes into the first result, everything that doesn’t goes into the second result.

// an array we want to split into two arrays based on a condition
const names = ['Michael', 'Jim', 'Dwight', 'Pam', 'Ryan']

// accepts array and function returning `true` or `false` for each element
const partition = (array, callback) => {
  const matches = []
  const nonMatches = []

  // push each element into array depending on return value of `callback`
  array.forEach(element => (callback(element) ? matches : nonMatches).push(element))

  return [matches, nonMatches]
}

// destructure matches as `shortNames` and non-matches as `longNames`
const [shortNames, longNames] = partition(names, name => name.length <= 3)
// ⇒ shortNames: ['Jim', 'Pam']
// ⇒ longNames:  ['Michael', 'Dwight', 'Ryan']

const [evenLength, oddLength] = partition(names, n => n.length % 2 === 0)
// ⇒ evenLength: ['Dwight', 'Ryan']
// ⇒ oddLength:  ['Michael', 'Jim', 'Pam']

Getting the intersection of two arrays

To find which elements appear in both of two arrays, walk through every element in the first array and check if it exists in the second. With Array.prototype.filter() and Array.prototype.includes(), we can do that in a single line.

const getIntersection = (first, second) => {
  return first.filter(element => second.includes(element))
}

getIntersection([6, 15, 19, 86, 4], [4, 19, 22, 92, 51, 37])
// ⇒ [19, 4]

Converting numerical arrays into relative fractions

We needed to convert an array of (positive) numbers into fractions of each other, with the largest number being 100 percent. This function turns all values into percentages between 0 and that largest value.

const toPercentages = numbers => {
  const largestNumber = Math.max(...numbers)

  return numbers.map(number => `${number / largestNumber * 100}%`)
}

// Because 16 is the largest number in the array, it is treated as 100%. All
// other values are based on that: 8 is 50% of 16, 4 is 25%, and so on.
toPercentages([0, 8, 12, 6, 16, 3, 0])
// ⇒ ['0%', '50%', '75%', '37.5%', '100%', '18.75%', '0%']

// Percentages are based on the largest value, which is 40 in this example.
toPercentages([8, 15, 21, 5, 10, 40])
// ⇒ ['20%', '37.5%', '52.5%', '12.5%', '25%', '100%']

Merging many objects

Object.assign() combined with the rest and spread operators can shallow-merge an arbitrary number of objects.

// because of the two `...`, the function accepts any number of objects
const merge = (...objects) => Object.assign({}, ...objects)

// we can merge two objects into a single one
merge({ id: 5 }, { title: 'This is nice', description: 'Very nice' })
// ⇒ {
//     id: 5,
//     title: 'This is nice',
//     description: 'Very nice'
//   }

// we can pass as many objects to the function as we want; in case of
// duplicate keys, the last value always wins
merge({ name: 'Joe' }, { age: 27, email: 'joe@swanson.com' }, { age: 43 })
// ⇒ {
//     name: 'Joe',
//     email: 'joe@swanson.com',
//     age: 43
//   }

Picking object properties

With Array.prototype.reduce(), we can extract only the properties of an object we want to pass along.

// we don’t want to pass all of this information along all the time
const user = {
  email: 'ckent@dailyplanet.com',
  password: 'i-am-superman',
  username: 'clarkkent'
}

// this function receives an object and a list of properties to extract
const pick = (object, props) => props.reduce((picked, prop) => ({
  ...picked,
  [prop]: object[prop]
}), {})

// we can use `pick` to get only the information we need
const condensedUser = pick(user, ['username', 'email'])
// ⇒ {
//     username: 'clarkkent',
//     email: 'ckent@dailyplanet.com'
//   }

Getting properties from objects that match a condition

Need to get an attribute of all objects in an array that match a condition? Chain Array.prototype.filter() and Array.prototype.map() to first filter the list to all the elements you want and then get the values you’re looking for.

// this array contains objects that have a `name` and `type`
const food = [
  {
    name: 'Apple',
    type: 'fruit'
  }, {
    name: 'Banana',
    type: 'fruit'
  }, {
    name: 'Cucumber',
    type: 'vegetable'
  }
]

// we can first get all objects that have their `type` set to 'fruit'
const fruit = food.filter(element => element.type === 'fruit')
// ⇒ [
//     {
//       name: 'Apple',
//       type: 'fruit'
//     }, {
//       name: 'Banana',
//       type: 'fruit'
//     }
//   ]

// we can then use `.map()` to extract their names
const fruitNames = fruit.map(element => element.name)
// ⇒ ['Apple', 'Banana']


// we can do both steps at once by chaining `.filter()` and `.map()`
const fruitNames = food
  .filter(element => element.type === 'fruit')  // get all fruit
  .map(element => element.name)                 // extract their names
// ⇒ ['Apple', 'Banana']

Counting letters in a string

Want to know how often each letter appears in a string? Turn it into a dictionary. Each letter is a key, each value is the number of times that letter appears in the string.

const string = 'Supercalifragilisticexpialidocious'

// split the string into an array of individual letters
const result = string.split('')
  // turn all letters into lowercase
  .map(letter => letter.toLowerCase())
  // walk through all letters and build a dictionary as you go
  .reduce((dictionary, letter) => ({
    // take the result after the previous step into the new result
    ...dictionary,
    // increment the existing counter (or 0 for new entries) for this letter
    [letter]: (dictionary[letter] ?? 0) + 1
  }), {})

console.log(result)
// ⇒ {
//     a: 3,  c: 3,  d: 1,  e: 2,  f: 1,  g: 1,  i: 7,  l: 3,
//     o: 2.  p: 2,  r: 2,  s: 3,  t: 1,  u: 2,  x: 1
//   }

Sorting numbers

JavaScript treats all elements in arrays as strings when sorting and orders them alphabetically. When sorting the numbers 1 through 10, 10 appears between 1 and 2. We can fix that with a custom compare function.

// default: 10 shows up early because “1” comes before “2” in the alphabet
[7, 2, 9, 1, 5, 10, 6, 4, 3, 8].sort()
// ⇒ [1, 10, 2, 3, 4, 5, 6, 7, 8, 9]

// better: this sorts the elements by their numerical value
[7, 2, 9, 1, 5, 10, 6, 4, 3, 8].sort((a, b) => a - b)
// ⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Checking if every array element matches a condition

To check if every element matches a condition, arrays in JavaScript have the appropriately named Array.prototype.every().

const isEverybodyOver18 = ages => ages.every(age => age > 18)

isEverybodyOver18([27, 35, 52, 91]) // ⇒ true
isEverybodyOver18([16, 23, 42, 19]) // ⇒ false

Splitting arrays into chunks

With a combination of Array.prototype.map() and Array.prototpe.slice(), we can split an array into many smaller arrays. This is useful for pagination or injecting something into an array every set number of elements. There’s an explanation of how this works on my blog: How to split arrays into equal-sized chunks

// `array` is the array we want to split, `chunkSize` is how big we want
// each group to be
const chunkArray = (array, chunkSize) => {
  // find how many chunks we need if each holds `chunkSize` elements
  const numberOfChunks = Math.ceil(array.length / chunkSize)

  // create an array with one slot for each chunk we need
  const chunks = [...Array(numberOfChunks)]
    // walk through each of the empty slots
    .map((value, index) => {
      // put a slice of the original `array` in the empty slot
      return array.slice(index * chunkSize, (index + 1) * chunkSize)
    })

  return chunks
}

console.log(chunkArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5))
// ⇒ [
//     [1, 2, 3, 4, 5],
//     [6, 7, 8, 9, 10]
//   ]

console.log(chunkArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 4))
// ⇒ [
//     [1, 2, 3, 4],
//     [5, 6, 7, 8],
//     [9, 10]
//   ]

Calculating sums

When “reducing” an array, JavaScript walks through all of its values and combines (reduces) them to a single value. We can use this to calculate the sum of all values in an array in a single line.

const numbers = [4, 8, 15, 16, 23, 42]


// we could `for`-loop over the array and calculate the sum as we go
let sumLooped = 0

for (let i = 0; i < numbers.length; i += 1) {
  sumLooped += numbers[i]
}


// `reduce` calculates the same sum, but in a single line
const sumReduced = numbers.reduce((sum, value) => sum + value, 0)


console.log({ sumLooped, sumReduced })
// ⇒ "{ sumLooped: 108, sumReduced: 108 }"

Logging as objects

By wrapping the variables in a console.log in curly brackets, it logs an object that uses the names of the variables as keys. That makes it easier to see what each value represents.

const apples = 6
const children = 3
const cars = 1

console.log(apples, children, cars)
// ⇒ "6, 3, 1"
//
// Wait, what does the `3` mean again?

console.log({ apples, children, cars })
// ⇒ "{ apples: 6, children: 3, cars: 1 }"
//
// The variables’ names act as labels here.

Using the ternary operator in assignment and return

The ternary operator can be used when assigning variables or even as part of return statements. It can help us make some arrow functions even shorter.

const isEven = n => n % 2 === 0

// assigning the result of a ternary operator to a variable
const numberType = isEven(35) ? 'even' : 'odd'

// returning the result of a ternary operator in a function
const getNumberType = number => {
  return isEven(number) ? 'even' : 'odd'
}

// returning the result of a ternary operator using an implicit return
const getNumberType = number => isEven(number) ? 'even' : 'odd'

Array methods shorthand

JavaScript’s array methods pass their values through to the functions given to them. If you would only forward those to another function, you can skip the arrow function and name that function directly.

const numbers = [4, 8, 15, 16, 23, 42]
const isEven = n => n % 2 === 0

numbers.filter(number => isEven(number))  // ⇒ [4, 8, 16, 42]
numbers.filter(isEven)                    // ⇒ [4, 8, 16, 42]

Destructuring in assignment

If the name of your variable is the exact same as the property you would extract from an object, you can use destructuring instead. That way, you don’t have to type the same word twice.

const person = {
  name: 'Bob',
  age: 35
}

// without destructuring
const age = person.age

// with destructuring
const { age } = person

The nullish-coalescing operator

In JavaScript, we can use || to set default values for variables. Besides null and undefined, this also overrides other falsy values like 0, false, and empty strings. The nullish coalescing operator ?? only overrides null and undefined.

       27 || "default"    // ⇒ 27           good
     true || "default"    // ⇒ true         good
"truthy!" || "default"    // ⇒ "truthy!"    good
     null || "default"    // ⇒ "default"    good
undefined || "default"    // ⇒ "default"    good
        0 || "default"    // ⇒ "default"    bad (overrides real value)
    false || "default"    // ⇒ "default"    bad (overrides real value)
       "" || "default"    // ⇒ "default"    bad (overrides real value)

       27 ?? "default"    // ⇒ 27           good
     true ?? "default"    // ⇒ true         good
"truthy!" ?? "default"    // ⇒ "truthy!"    good
     null ?? "default"    // ⇒ "default"    good
undefined ?? "default"    // ⇒ "default"    good
        0 ?? "default"    // ⇒ 0            good (keeps real value)
    false ?? "default"    // ⇒ false        good (keeps real value)
       "" ?? "default"    // ⇒ ""           good (keeps real value)

Rewriting switch statements to lookup objects

Some switch-statements can be replaced with lookup-tables in JavaScript. If every case immediately returns a value, this pattern achieves the same in fewer lines of code.

// before: switching through the values to return a number requires a lot of code
const getFeetForAnimal = animal => {
  switch (animal) {
    case 'bird':
      return 2
    case 'dog':
      return 4
    case 'fish':
      return 0
    case 'spider':
      return 8
  }
}

// after: a lookup object and implicit `return` do the same in fewer lines
const getFeetForAnimal = animal => ({
  bird: 2,
  dog: 4,
  fish: 0,
  spider: 8
})[animal]

getFeetForAnimal('dog')     // ⇒ 4
getFeetForAnimal('gorilla') // ⇒ undefined (does not exist in lookup table)

Removing duplication with higher-order functions

By splitting a list of parameters into groups, we end up writing less, more readable code. We can also use this to quickly create many similar functions.

// We could write a function that gives us the correct pluralization of a
// word for a given amount like this:
const pluralize = (singular, plural, count) => {
  return count === 1 ? singular : plural
}

// We would call it like this, but we would have to repeat a lot of values:
pluralize('dog', 'dogs', 0) // ⇒ 'dogs'
pluralize('dog', 'dogs', 1) // ⇒ 'dog'
pluralize('dog', 'dogs', 2) // ⇒ 'dogs'


// Let’s split the parameters into two groups, adding an arrow between them:
const pluralize = (singular, plural) => count => {
  return count === 1 ? singular : plural
}

// Because we have two arrows now, we also need two sets of parentheses.
// This works exactly as before, but so far looks like _more_ work for us:
pluralize('dog', 'dogs')(0) // ⇒ 'dogs'
pluralize('dog', 'dogs')(1) // ⇒ 'dog'
pluralize('dog', 'dogs')(2) // ⇒ 'dogs'

// Because this is really two function calls, we can extract the first one:
const pluralizeDog = pluralize('dog', 'dogs')

// With this, we don’t have to repeat 'dog' and 'dogs' every time:
pluralizeDog(0) // ⇒ 'dogs'
pluralizeDog(1) // ⇒ 'dog'
pluralizeDog(2) // ⇒ 'dogs'

// We can use this to make all kinds of pluralization-functions:
const pluralizeTable = pluralize('table', 'tables')
const pluralizeHouse = pluralize('house', 'houses')
const pluralizeSheep = pluralize('sheep', 'sheep')

Counting months from zero

Many programming languages start counting months one number before we would, which sounds odd at first. It’s like saying January is “month zero” and December is “month eleven”.

const months = [
  'January', 'February', 'March',     'April',   'May',      'June',
  'July',    'August',   'September', 'October', 'November', 'December'
]

// `.length` tells us how many months there are in a year.
months.length // ⇒ 12

// Counting in an array start at 0, so the months are numbered from 0 to 11.
// A year still has 12 months, but we need to shift their index down by 1.

// While January is the 1st month for us, it’s the 0th element in the array.
months[0] // ⇒ 'January'

// The index of December is also one less than what we would say.
months[11] // ⇒ 'December'

// The month at the index 12 would be the 13th month, which does not exist.
months[12] // ⇒ undefined

Switching between functions with a ternary operator

If we use a condition to choose which of two functions to call with the same parameters, we can use a ternary operator to remove some repetition.

// Regardless of which function we pick, we always pass 'martini' to it.
if (isJamesBond) {
  shake('martini')
} else {
  stir('martini')
}

// Because the parameter stays the same, we can select the function with a
// ternary operator instead.
(isJamesBond ? shake : stir)('martini')

Error handling with try-catch

With try and catch, we can react to errors in functions that could fail without our code crashing. If there is no error and the function succeeds, the catch-block is skipped.

// `try` to do something that could fail
try {
  // this may or may not be possible
  makeItBeFriday()

  // if we get here, the function worked
  console.log('It is Friday now!')
} catch (error) {
  // if something in `try` fails and throws an error, we end up here
  console.log('Sorry, it is not Friday now.')
}

Accessing object properties with bracket notation

In JavaScript, we can use bracket notation to access an object’s property whose name is stored in a variable.

const storage = {
  candy: 5,
  chocolate: 6
}

// We can access properties directly by name using dot-notation. We can also
// access them by writing their name as a string in square brackets:
storage.candy    // ⇒ 5
storage['candy'] // ⇒ 5

// If we don’t know the name of the property because it only exists in a
// variable, we _have_ to use bracket notation:
const lookUp = 'chocolate'
storage.lookUp  // ⇒ undefined (looks for missing property 'lookUp')
storage[lookUp] // ⇒ 6

Splitting strings

When we split a string, the element we split at does not appear in the result. If we split at every empty string, we get the individual characters (including spaces).

// If we split at “space”, no spaces appear in the result:
'two words'.split(' ')           // ⇒ ['two', 'words']

// Periods and other characters still remain:
'This is a sentence.'.split(' ') // ⇒ ['This', 'is', 'a', 'sentence.']

// If we split at a letter, that letter is removed. The space remains:
'hello world'.split('r')         // ⇒ ['hello wo', 'ld']

// If we split at “empty space”, the string is split into all of its
// characters. The space is still in there:
'oh yeah'.split('')              // ⇒ ['o', 'h', ' ', 'y', 'e', 'a', 'h']

Passing parameters as a single object

We can make a function more readable by passing it a single object instead of independent parameters. Every time we call this function, we can see what each parameter means by its key on that object.

// This function takes five parameters. In the body of the function, they
// can be accessed with names that describe what each value represents.
const hostGuest = (age, hoursAwake, isHungry, isTired, name) => {
  if (isTired) {
    // let them rest
  } else if (isHungry) {
    // give them food
  }
}

// When using that function, that descriptiveness is lost. Without looking
// at the function, we have to remember what each value means. Is John 30 or
// 19? Are they hungry or tired?
hostGuest(30, 19, false, true, 'John')


// By wrapping the parameters in curly brackets, the function now accepts a
// single object instead of five independent values. We can still access
// them exactly as we did before.
const hostGuest = ({ age, hoursAwake, isHungry, isTired, name }) => {
  if (isTired) {
    // let them rest
  } else if (isHungry) {
    // give them food
  }
}

// When using _this_ function, we have to pass it an object. The keys of
// that object help us describe what attribute each value represents.
hostGuest({
  age: 30,
  hoursAwake: 19,
  isHungry: false,
  isTired: true,
  name: 'John'
})

Implicit return in arrow functions

If a JavaScript arrow function immediately returns a value, writing return is optional (if we also remove the curly brackets). The return is then implied, making it an “implicit return”.

// we could write the `return` explicitly
const getPumpedAbout = thing => {
  return `Pumped about ${thing}!`
}

// if we write it like this, the `return` is implied
const getPumpedAbout = thing => `Pumped about ${thing}!`

// there is no difference in how we would call these functions
getPumpedAbout('implicit returns') // ⇒ 'Pumped about implicit returns!'

Hiding repetition with helper functions

Have to use a function over and over, but one or more of its parameters are always the same? Improve readability by creating a function that hides that repetition.

// Repeating `en_US` every time is tedious and prone to typos.
const title = translate('app.title', 'en_US')
const slogan = translate('app.slogan', 'en_US')
const action = translate('app.action', 'en_US')

// While `translate` takes two parameters, this function only takes one. It
// returns the result we would get if we called `translate` with `en_US`.
const translateEN = key => translate(key, 'en_US')

// We can call our function as before, but no longer need to write `en_US`.
const title = translateEN('app.title')
const slogan = translateEN('app.slogan')
const action = translateEN('app.action')

Interpolation in template literals

Backticks turn strings into “template literals” in JavaScript. They allow you to interpolate variables, the results of calculations, and even the return values of function calls.

const age = 4

// the “old” way to get the value of `age` into a strings
const concatenation = 'Little Bobby is ' + age + ' years old.'

// use `${expression}` to do the same in a template literal
const interpolation = `Little Bobby is ${age} years old.`

// an expression can be a calculation
const withCalculation = `Next year, Bobby will be ${age + 1} years old.`

// you can even use the return value of a function call in an expression
const withFunctionCall = `${age} years are ${yearsInDays(age)} days.`

Namespacing styled-components

If you keep your styled-components in a separate file, you can import them under a namespace like “ui”. If a component’s name then begins with ui., you can then tell it is one of your styled-components.

/* ./MyComponent/styles.js */
import styled from 'styled-components'

export const Title = styled.h1`
  font-size: 2rem;
  line-height: 1.35;
`

export const Description = styled.p`
  font-size: 1.6rem;
  line-height: 1.5;
`
/* ./MyComponent/index.js */
import React from 'react'
import SomeOtherComponent from '../SomeOtherComponent'
import * as ui from './styles'

const MyComponent = ({ title, description }) => {
  return (
    <div>
      <ui.Title>
        {title}
      </ui.Title>

      <ui.Description>
        {description}
      </ui.Description>

      <SomeOtherComponent />
    </div>
  )
}

export default MyComponent

Creating number ranges

To get a range of numbers in JavaScript, initialize an array by spreading the keys of another array into it. You can shift the range up or down, or have it contain only even numbers.

const range = [...Array(5).keys()] // ⇒ [0, 1, 2, 3, 4]

// the number in `Array(number)` describes how many values you want
[...Array(7).keys()] // ⇒ [0, 1, 2, 3, 4, 5, 6]

// you can `map` the values to shift or otherwise manipulate the range
[...Array(4).keys()].map(n => n + 3) // ⇒ [3, 4, 5, 6]
[...Array(4).keys()].map(n => n - 3) // ⇒ [-3, -2, -1, 0]
[...Array(4).keys()].map(n => n * 2) // ⇒ [0, 2, 4, 6]

Implicit object return in arrow functions

If your arrow function immediately returns an object and does nothing else, you can wrap that object in parentheses instead of writing return.

// we can be explicit and write `return` before the object we want to return
const myFunction = value => {
  return {
    key: value,
    length: value.length
  }
}

// wrapping the object in parentheses also returns it
const myFunction = value => ({
  key: value,
  length: value.length
})

// if anything else happens in the function, like assigning variables or
// calling other functions, we HAVE to be explicit and write `return`
const myFunction = value => {
  const length = value.length

  return {
    key: value,
    length: length
  }
}

Combining arrow functions in styled-components

Instead of using many arrow functions to extract the theme in your styled-components, you can group them and do them all in one block.

const Button = styled.button`
  box-shadow: ${({ theme }) => theme.boxShadows.medium};
  color: ${({ theme }) => theme.colors.white};
  font-weight: ${({ theme }) => theme.fontWeights.semibold};
  margin: 0;
`;

const Button = styled.button`
  ${({ theme }) => `
    box-shadow: ${theme.boxShadows.medium};
    color: ${theme.colors.white};
    font-weight: ${theme.fontWeights.semibold};
  `}
  margin: 0;
`;

Destructuring props in styled-components

Find yourself writing props. over and over in your styled-components? Destructure them to reduce the noise.

const Post = styled.article`
  background: ${props =>
    props.isFeatured ? props.theme.yellow : props.theme.white
  };
`;

const Post = styled.article`
  background: ${({ isFeatured, theme }) =>
    isFeatured ? theme.yellow : theme.white
  };
`;

Comparing to null with double-equals

Compare a variable to null using two equal signs to check if it is null or undefined at the same time.

if (value === null || value === undefined) {
  // `value` is `null` or `undefined`
}

if (value == null) {
  // `value` is `null` or `undefined`
}

Fire tips tagged “performance

Accessing array elements faster with lookup objects

If you identify objects in an array by a unique property, turn the array into a lookup object. The unique property becomes a key, giving you access to the objects without having to search for them in the array first.

// this array holds objects that all have name- and type-properties
const array = [
  { name: 'Bulbasaur',  type: 'Grass/Poison' },
  { name: 'Charmander', type: 'Fire'         },
  { name: 'Squirtle',   type: 'Water'        },
  { name: 'Pikachu',    type: 'Electric'     }
]

// to get the type for a name, we need to .find() the element first
array.find(pokemon => pokemon.name === 'Charmander').type
// ⇒ 'Fire'


// this lookup object uses the unique names as keys
const object = {
  'Bulbasaur':  { name: 'Bulbasaur',  type: 'Grass/Poison' },
  'Charmander': { name: 'Charmander', type: 'Fire'         },
  'Squirtle':   { name: 'Squirtle',   type: 'Water'        },
  'Pikachu':    { name: 'Pikachu',    type: 'Electric'     }
}

// lookups work with bracket notation, which is faster than .find()
object['Charmander'].type
// ⇒ 'Fire'

Fire tips tagged “regular expressions

Extracting floats from strings

To get all float values from a sentence, we can first find them with a regular expression and then map over the matches with parseFloat.

// matches all integer or decimal substrings, like “69” or “4.20”
const numbersRegexp = new RegExp(/\d+(\.\d+)?/, 'g')

// finds all numbers matching the expression and transforms them to numbers
const getFloatsFrom = string => string.match(numbersRegexp).map(parseFloat)

getFloatsFrom("Put the 16 cupcakes in the oven (at 180°C) for 1.5 hours.")
// ⇒ [16, 180, 1.5]

Replacing all matches in a string

When replacing a substring in a string, only the first match is replaced. If we want to replace all matches, we need to use a regular expression and tell it to match globally within the string.

// if the first parameter is a string, only the first match is replaced
'Pika pika!'.replace('ka', 'dgey')   // ⇒ 'Pidgey pika!'

// we can write the pattern as a regular expression, which achieves the same
'Pika pika!'.replace(/ka/, 'dgey')   // ⇒ 'Pidgey pika!'

// with the additional `g`-flag, all matches (globally) are replaced
'Pika pika!'.replace(/ka/g, 'dgey')  // ⇒ 'Pidgey pidgey!'

Splitting strings by lengths

JavaScript can split strings at given characters. By matching against a regular expression, we can split strings into substrings of a given length instead.

// split at every `O`; all `O`s are gone in the result
'BRBIDKLOLOMGOK'.split('O')  // ⇒ ['BRBIDKL', 'L', 'MG', 'K']

// split into groups of up to 3 characters (the last group can be shorter)
'BRBIDKLOLOMGOK'.match(/.{1,3}/g)  // ⇒ ['BRB', 'IDK', 'LOL', 'OMG', 'OK']

Fire tips tagged “readability

Removing duplication with ternary operators

One use of the ternary operator is to reduce duplication. If we’d call the same function with a different parameter in each case, we can move the ternary into the function call.

Watch out for readability: the shortest version isn’t always the best option.

// we want to wish people a happy weekend/workweek depending on the day
const isWeekend = day === 'Saturday' || day === 'Sunday'

// we can call `console.log` with the respective message like this
isWeekend ? console.log('Happy weekend!') : console.log('Happy workweek!')

// `console.log` happens in any case, so we can move the ternary inside it
console.log(isWeekend ? 'Happy weekend!' : 'Happy workweek!')

// we can extract the duplicate “Happy ” and “!” to only write them once
console.log(`Happy ${isWeekend ? 'weekend' : 'workweek'}!`)

// don’t go crazy and extract EVERY duplication; the code becomes shorter
// if we extract the duplicate “w”, but also much harder to read
console.log(`Happy w${isWeekend ? 'eekend' : 'orkweek'}!`)

Redundant comparisons to true or false

If an expression already returns a Boolean value, comparing that result to true or false is redundant. Leave it out to make your code shorter and more readable.

// before: comparing the Boolean result to `true` or `false` is redundant
if (number < 10 === true) { /* code */ }

// after: using the comparison’s result directly is shorter and more readable
if (number < 10) { /* code */ }


// before: the ternary operator is redundant in this assignment as well
const isHello = string === 'Hello' ? true : false

// after: the comparison is already `true` or `false`, which we can use directly
const isHello = string === 'Hello'


// before: this if-else-block only returns the result of a conditional chain
const isTheWeekend = day => {
  if (day === 'Saturday' || day === 'Sunday') {
    return true
  } else {
    return false
  }
}

// after: our function can return the value of the conditional chain directly
const isTheWeekend = day => day === 'Saturday' || day === 'Sunday'

Assignment operators shorthand

JavaScript has many different assignment operators that are shorthand for longer versions. += is one of the more common ones. Most other calculations also have an assignment operator.

// regular assignment assigns the right value to the variable on the left
a = b

// we can shorten the code if the variable is itself part of the calculation
a = a + 5    // is the same as:
a += 5

// these are some of the available shorthand assignment operators
a += b    // a = a + b       addition assignment
a -= b    // a = a - b       subtraction assignment
a *= b    // a = a * b       multiplication assignment
a /= b    // a = a / b       division assignment
a %= b    // a = a % b       remainder assignment

a &&= b   // a = a && b      logical AND assignment
a ||= b   // a = a || b      logical OR assignment

Extracting complex logic to named functions

If a piece of code looks cryptic and is difficult to understand, put it in a function. By giving that function a descriptive name, your code becomes much more readable. A good name can make a long comment unnecessary.

// We can get today’s date in a specific timezone with `.toLocaleString()`.
// The code looks complicated, and we might not remember what it does two
// months from now.

const now = new Date()
const nowAsString = now.toLocaleString('en-US', {
  timeZone: 'America/Los_Angeles'
})
nowAsString.match(/\d{1,2}\/\d{1,2}\/\d{4}/)[0]
// ⇒ '8/15/2020'

// Putting this code into a function makes it easily reusable. We don’t need
// to write a comment for it because the name already explains what it does.
// It’s okay to condense the code and skip a few variables in this case.

const getDateInLosAngeles = () => (new Date()).toLocaleString('en-US', {
  timeZone: 'America/Los_Angeles'
}).match(/\d{1,2}\/\d{1,2}\/\d{4}/)[0]

// Getting the date with one function call is more convenient and readable.
getDateInLosAngeles()
// ⇒ '8/15/2020'

Composing functions to run in sequence

If you need to nest functions, or store their output only to pass it to another function, you can instead compose a function that runs an input through many functions in sequence.

// these functions all take a string and do something with it
const reverse = name => [...name].reverse().join('')
const upcase = name => name.toUpperCase()
const greet = name => `Hi, ${name}!`

// we could store each result and pass it to the next function
const reversed = reverse('john')
const upcased = upcase(reversed)
greet(upcased)
// ⇒ 'Hi, NHOJ!'

// if we nest them like this instead, they are run from inside to outside
greet(upcase(reverse('john')))
// ⇒ 'Hi, NHOJ!'

// this takes several functions and runs an input through them in sequence
const compose = (...fns) => input => {
  return fns.reduce((result, fn) => fn(result), input)
}

// with our `compose`, we can list the functions and call them like this
compose(reverse, upcase, greet)('john')
// ⇒ 'Hi, NHOJ!'

// the order of functions defines the order the steps are executed in
compose(greet, reverse, upcase)('john')
// ⇒ '!NHOJ ,IH'

// we can also assign our composed function to a variable and reuse it
const sequence = compose(upcase, greet, reverse)
sequence('john')  // ⇒ '!NHOJ ,iH'
sequence('paul')  // ⇒ '!LUAP ,iH'

The optional-chaining operator

We can get nested values from objects by chaining their keys. We run into an error when we try to get a value from something that does not exist. With optional chaining, these calls return undefined instead of throwing errors.

// this person’s address is nested in the outer object as another object
const mario = {
  occupation: 'Plumber',
  address: {
    street: 'Rainbow Road',
    zip: '81664'
  }
}

// we can get the full address, or drill into it to get the street
mario.address         // ⇒ { street: 'Rainbow Road': zip: '81664' }
mario.address.street  // ⇒ 'Rainbow Road'

// we don’t have a nested address for this person
const toad = {
  occupation: 'Explorer'
}

// if we try to get the street, JavaScript will throw an error
toad.address         // ⇒ undefined
toad.address.street  // TypeError: undefined is not an object

// the ?. operator returns `undefined` if it hits any undefined value
toad?.address          // ⇒ undefined (same as without the questionmark)
toad?.address?.street  // ⇒ undefined (no error!)

Plucking properties from object arrays

One of the more common uses for Array.prototype.map() is extracting properties from objects. Instead of using individual arrow functions, we can create a reusable function that does the plucking for us.

const countries = [
  { name: 'France', capital: 'Paris'  },
  { name: 'Spain',  capital: 'Madrid' },
  { name: 'Italy',  capital: 'Rome'   }
]

// we can extract the attributes with individual arrow functions
countries.map(country => country.name)     // ⇒ ['France', 'Spain', 'Italy']
countries.map(country => country.capital)  // ⇒ ['Paris', 'Madrid', 'Rome']

// this function allows us to write that arrow function shorter
const pluck = property => element => element[property]

countries.map(pluck('name'))     // ⇒ ['France', 'Spain', 'Italy']
countries.map(pluck('capital'))  // ⇒ ['Paris', 'Madrid', 'Rome']

Array methods shorthand

JavaScript’s array methods pass their values through to the functions given to them. If you would only forward those to another function, you can skip the arrow function and name that function directly.

const numbers = [4, 8, 15, 16, 23, 42]
const isEven = n => n % 2 === 0

numbers.filter(number => isEven(number))  // ⇒ [4, 8, 16, 42]
numbers.filter(isEven)                    // ⇒ [4, 8, 16, 42]

Destructuring in assignment

If the name of your variable is the exact same as the property you would extract from an object, you can use destructuring instead. That way, you don’t have to type the same word twice.

const person = {
  name: 'Bob',
  age: 35
}

// without destructuring
const age = person.age

// with destructuring
const { age } = person

Rewriting switch statements to lookup objects

Some switch-statements can be replaced with lookup-tables in JavaScript. If every case immediately returns a value, this pattern achieves the same in fewer lines of code.

// before: switching through the values to return a number requires a lot of code
const getFeetForAnimal = animal => {
  switch (animal) {
    case 'bird':
      return 2
    case 'dog':
      return 4
    case 'fish':
      return 0
    case 'spider':
      return 8
  }
}

// after: a lookup object and implicit `return` do the same in fewer lines
const getFeetForAnimal = animal => ({
  bird: 2,
  dog: 4,
  fish: 0,
  spider: 8
})[animal]

getFeetForAnimal('dog')     // ⇒ 4
getFeetForAnimal('gorilla') // ⇒ undefined (does not exist in lookup table)

Removing duplication with higher-order functions

By splitting a list of parameters into groups, we end up writing less, more readable code. We can also use this to quickly create many similar functions.

// We could write a function that gives us the correct pluralization of a
// word for a given amount like this:
const pluralize = (singular, plural, count) => {
  return count === 1 ? singular : plural
}

// We would call it like this, but we would have to repeat a lot of values:
pluralize('dog', 'dogs', 0) // ⇒ 'dogs'
pluralize('dog', 'dogs', 1) // ⇒ 'dog'
pluralize('dog', 'dogs', 2) // ⇒ 'dogs'


// Let’s split the parameters into two groups, adding an arrow between them:
const pluralize = (singular, plural) => count => {
  return count === 1 ? singular : plural
}

// Because we have two arrows now, we also need two sets of parentheses.
// This works exactly as before, but so far looks like _more_ work for us:
pluralize('dog', 'dogs')(0) // ⇒ 'dogs'
pluralize('dog', 'dogs')(1) // ⇒ 'dog'
pluralize('dog', 'dogs')(2) // ⇒ 'dogs'

// Because this is really two function calls, we can extract the first one:
const pluralizeDog = pluralize('dog', 'dogs')

// With this, we don’t have to repeat 'dog' and 'dogs' every time:
pluralizeDog(0) // ⇒ 'dogs'
pluralizeDog(1) // ⇒ 'dog'
pluralizeDog(2) // ⇒ 'dogs'

// We can use this to make all kinds of pluralization-functions:
const pluralizeTable = pluralize('table', 'tables')
const pluralizeHouse = pluralize('house', 'houses')
const pluralizeSheep = pluralize('sheep', 'sheep')

Omitting optional else-branches

Sometimes, the else in an if-else is optional. When the if-condition makes the function end, we don’t have to nest the alternative case in else at all.

const getWeekdayMood = isMonday => {
  if (isMonday) {
    // If the condition is `true`, the function returns a value and ends.
    return 'Yes, a new week!'
  } else {
    // This code only happens if the condition is `false`.
    return 'Time for another Monday soon!'
  }
}


const getWeekdayMood = isMonday => {
  if (isMonday) {
    // Same deal: if this returns, the function ends. Nothing else happens.
    return 'Yes, a new week!'
  }

  // Anything here also only happens if the condition is `false`. We don’t
  // need to nest our code in an `else` for that.

  // Look ma, no `else`!
  return 'Time for another Monday soon!'
}

Passing parameters as a single object

We can make a function more readable by passing it a single object instead of independent parameters. Every time we call this function, we can see what each parameter means by its key on that object.

// This function takes five parameters. In the body of the function, they
// can be accessed with names that describe what each value represents.
const hostGuest = (age, hoursAwake, isHungry, isTired, name) => {
  if (isTired) {
    // let them rest
  } else if (isHungry) {
    // give them food
  }
}

// When using that function, that descriptiveness is lost. Without looking
// at the function, we have to remember what each value means. Is John 30 or
// 19? Are they hungry or tired?
hostGuest(30, 19, false, true, 'John')


// By wrapping the parameters in curly brackets, the function now accepts a
// single object instead of five independent values. We can still access
// them exactly as we did before.
const hostGuest = ({ age, hoursAwake, isHungry, isTired, name }) => {
  if (isTired) {
    // let them rest
  } else if (isHungry) {
    // give them food
  }
}

// When using _this_ function, we have to pass it an object. The keys of
// that object help us describe what attribute each value represents.
hostGuest({
  age: 30,
  hoursAwake: 19,
  isHungry: false,
  isTired: true,
  name: 'John'
})

Hiding repetition with helper functions

Have to use a function over and over, but one or more of its parameters are always the same? Improve readability by creating a function that hides that repetition.

// Repeating `en_US` every time is tedious and prone to typos.
const title = translate('app.title', 'en_US')
const slogan = translate('app.slogan', 'en_US')
const action = translate('app.action', 'en_US')

// While `translate` takes two parameters, this function only takes one. It
// returns the result we would get if we called `translate` with `en_US`.
const translateEN = key => translate(key, 'en_US')

// We can call our function as before, but no longer need to write `en_US`.
const title = translateEN('app.title')
const slogan = translateEN('app.slogan')
const action = translateEN('app.action')

Namespacing styled-components

If you keep your styled-components in a separate file, you can import them under a namespace like “ui”. If a component’s name then begins with ui., you can then tell it is one of your styled-components.

/* ./MyComponent/styles.js */
import styled from 'styled-components'

export const Title = styled.h1`
  font-size: 2rem;
  line-height: 1.35;
`

export const Description = styled.p`
  font-size: 1.6rem;
  line-height: 1.5;
`
/* ./MyComponent/index.js */
import React from 'react'
import SomeOtherComponent from '../SomeOtherComponent'
import * as ui from './styles'

const MyComponent = ({ title, description }) => {
  return (
    <div>
      <ui.Title>
        {title}
      </ui.Title>

      <ui.Description>
        {description}
      </ui.Description>

      <SomeOtherComponent />
    </div>
  )
}

export default MyComponent

Replacing complex conditions with descriptive variables

A few well-named variables go a long way towards making code more self-explanatory. Readability is worth a few extra lines; your future self will thank you!

// before: it’s hard to see what this combination of conditions does
if (!(currentDay === 'Saturday' || currentDay === 'Sunday') &&
    currentHour >= 9 && currentHour <= 18) {
  return 'The store is open!'
}


// after: by assigning the conditions to variables, we can use their names
// to explain the logic behind our code in plain language
const isSaturday = currentDay === 'Saturday'
const isSunday = currentDay === 'Sunday'

const isWeekend = isSaturday || isSunday

const isDuringBusinessHours = currentHour >= 9 && currentHour <= 18

if (!isWeekend && isDuringBusinessHours) {
  return 'The store is open!'
}

Using Boolean prefixes for variables

Prefixes like “is”, “has”, or “does” help emphasize that a variable holds a Boolean value.

if (monday) {
  // `monday` could be the date of next Monday. You wouldn’t expect this to
  // be `true` or `false`.
}

if (isMonday) {
  // This reads like the question “is Monday?”, which you would answer with
  // “yes” or “no”. The name tells you that this has to be Boolean.
}


if (children) {
  // `children` could be a list of names or whether a person has children or
  // not. It’s difficult to tell if this is an array or a Boolean.
}

if (hasChildren) {
  // You can only read this as “has children?”, which is either `true` or
  // `false`. Ideally.
}


if (rocks) {
  // Could be the name of your favorite rocks, like “The” or “30”.
}

if (doesRock) {
  // This does rock, `true`. Note how it’s not called `doesRocks`, which
  // would read a little awkward.
}

Fire tips tagged “algorithms

Shuffling arrays

The Fisher-Yates shuffle is a fast, unbiased way to shuffle arrays. It works by starting from the back, swapping the last element with one before it. This is repeated while moving towards the front until every element has been swapped at least once.

const shuffle = array => {
  // create a clone of the array that we can rearrange in place
  const copy = array.slice()

  // start at the last element, moving towards the front with every repetition
  for (let i = copy.length - 1; i > 0; i--) {
    // get the index of an element left of (and including) the current one
    const j = Math.floor(Math.random() * (i + 1))

    // swap the values at the positions i and j
    const temp = copy[i]
    copy[i] = copy[j]
    copy[j] = temp
  }

  return copy
}

shuffle([1, 2, 3, 4])  // ⇒ [4, 1, 2, 3] (maybe)
shuffle([1, 2, 3, 4])  // ⇒ [3, 1, 2, 4] (maybe)
shuffle([1, 2, 3, 4])  // ⇒ [1, 4, 2, 3] (maybe)
shuffle([1, 2, 3, 4])  // ⇒ [3, 2, 1, 4] (maybe)

Fire tips tagged “Sass

Creating utility classes with Sass

Sass’ @each-loop can create many similar looking CSS-classes quickly. We can use that to create our own utility-first classes like they use in Tailwind CSS.

$font-sizes: (
  "xs": 1.2rem,
  "s":  1.6rem,
  "m":  2.0rem,
  "l":  3.2rem,
  "xl": 4.8rem
);

@each $size, $value in $font-sizes {
  .font-size-#{$size} {
    font-size: $value;
  }
}

/*
  .font-size-xs { font-size: 1.2rem; }
  .font-size-s  { font-size: 1.6rem; }
  .font-size-m  { font-size: 2.0rem; }
  .font-size-l  { font-size: 3.2rem; }
  .font-size-xl { font-size: 4.8rem; }
*/

Fire tips tagged “utility-first CSS

Creating utility classes with Sass

Sass’ @each-loop can create many similar looking CSS-classes quickly. We can use that to create our own utility-first classes like they use in Tailwind CSS.

$font-sizes: (
  "xs": 1.2rem,
  "s":  1.6rem,
  "m":  2.0rem,
  "l":  3.2rem,
  "xl": 4.8rem
);

@each $size, $value in $font-sizes {
  .font-size-#{$size} {
    font-size: $value;
  }
}

/*
  .font-size-xs { font-size: 1.2rem; }
  .font-size-s  { font-size: 1.6rem; }
  .font-size-m  { font-size: 2.0rem; }
  .font-size-l  { font-size: 3.2rem; }
  .font-size-xl { font-size: 4.8rem; }
*/

Creating state-selectors in utility-first CSS

Want to create state-prefixed CSS classes that only apply on hover, focus, or other states? Repeat that same pseudo-selector at the end of your selector.

<style>
  .focus\:yellow:focus {
    background: yellow;
  }

  .hover\:orange:hover {
    background: orange;
  }
</style>

<button class="focus:yellow hover:orange">
  Yellow on focus, orange on hover
</button>

Using non-alphanumeric characters in CSS

You can use non-standard characters like “:” in CSS class names if you escape them with a backslash.

<style>
  .text\:italic {
    font-style: italic;
  }

  .text\:uppercase {
    text-transform: uppercase;
  }
</style>

<p class="text:italic text:uppercase">
  Italic and uppercase
</p>

Fire tips tagged “styled-components

Namespacing styled-components

If you keep your styled-components in a separate file, you can import them under a namespace like “ui”. If a component’s name then begins with ui., you can then tell it is one of your styled-components.

/* ./MyComponent/styles.js */
import styled from 'styled-components'

export const Title = styled.h1`
  font-size: 2rem;
  line-height: 1.35;
`

export const Description = styled.p`
  font-size: 1.6rem;
  line-height: 1.5;
`
/* ./MyComponent/index.js */
import React from 'react'
import SomeOtherComponent from '../SomeOtherComponent'
import * as ui from './styles'

const MyComponent = ({ title, description }) => {
  return (
    <div>
      <ui.Title>
        {title}
      </ui.Title>

      <ui.Description>
        {description}
      </ui.Description>

      <SomeOtherComponent />
    </div>
  )
}

export default MyComponent

Combining arrow functions in styled-components

Instead of using many arrow functions to extract the theme in your styled-components, you can group them and do them all in one block.

const Button = styled.button`
  box-shadow: ${({ theme }) => theme.boxShadows.medium};
  color: ${({ theme }) => theme.colors.white};
  font-weight: ${({ theme }) => theme.fontWeights.semibold};
  margin: 0;
`;

const Button = styled.button`
  ${({ theme }) => `
    box-shadow: ${theme.boxShadows.medium};
    color: ${theme.colors.white};
    font-weight: ${theme.fontWeights.semibold};
  `}
  margin: 0;
`;

Destructuring props in styled-components

Find yourself writing props. over and over in your styled-components? Destructure them to reduce the noise.

const Post = styled.article`
  background: ${props =>
    props.isFeatured ? props.theme.yellow : props.theme.white
  };
`;

const Post = styled.article`
  background: ${({ isFeatured, theme }) =>
    isFeatured ? theme.yellow : theme.white
  };
`;