Collection Operations

Collection operations are functions which operate on a collection (arrays and objects).

Javascript has some of these functions built-in, but some are not. So we use a 'library' called lodash for the functions which are not built-in.

A library is code written by someone else that we use in our project. Usually this code is 'open source' which just means the code is freely available. lodash is open source and the code is available on Github.

Filter

Filter is a function which is built onto arrays on javascript. It returns a clone of the array but with some of the elements 'filtered' out.

The filter( function takes a parameter which is itself a function. This function is run for each element in the array, if the function returns true that element is included in the array returned by filter( otherwise it is 'filtered' out.

Example:

filter( does not modify the array:

const array = [1, 2, 3]
const newArray = array.filter((number) => {
  return number > 1
})
print(array)
// prints: [1, 2, 3] array is unmodified
print(newArray)
// prints: [2, 3]

In the examples above the type of the function is: number => boolean. It takes a number type and returns a boolean (true/false)

If the function evaluates to false then we 'filter' that element out of the result.

Map

Map is similar to filter, it's built onto arrays in javascript. It also takes a function as a parameter, and it clones the array and doesn't modify the original.

Whereas filter returns an array with the same 'shape' as the original. Map allows you to 'map' each value to a new value.

Here in our function we add one to each value. This results in an array of the same length (3) but with each value 1 larger than the original:

const array = [1, 2, 3]
const newArray = array.map((number) => {
  return number + 1
})
print(newArray)
// prints: [2, 3, 4]

Map let's you change type completely. In this example array is an array of numbers, but the output of the .map( function is an array of strings.

const array = [1, 2, 3]
const newArray = array.map((number) => {
  return `Hello ${number}`
})
print(newArray)
// prints: ['Hello 1', 'Hello 2', 'Hello 3']  - an array of strings!

We'll learn some more collection operations shortly.

Chaining

Because functions like .filter( and .map( clone and return a new array they can be 'chained' together - this is good!

Here we do a .filter( followed by .map( on the output of the .filter(:

const array = [1, 2, 3]
array
  .filter((number) => {
    return number > 1
  })
  .map((number) => {
    return `Hello ${number}`
  })
// => ['Hello 2', 'Hello 3']

If possible, it's often nice to use the shorter function syntax. For example the above example can also be written:

const array = [1, 2, 3]
array.filter((number) => number > 1).map((number) => `Hello ${number}`)

// => ['Hello 2', 'Hello 3']

Spread and Collection Operators

Collection operators work great with spread .... Both avoid modifying things, which is good!

In this example we add a new key value pair area to each object in the array.

const rectangles = [
  {side1Length: 1, side2Length: 2},
  {side1Length: 3, side2Length: 3}
]
rectangles.map((r) => {
  return {...r, area: r.side1Length * r.side2Length}
})
// => [
//   { side1Length: 1, side2Length: 2, area: 2 },
//   { side1Length: 3, side2Length: 3, area: 9 }
// ]

If you'd like to do the above code on a single line you need to be careful:

const rectangles = [
  {side1Length: 1, side2Length: 2},
  {side1Length: 3, side2Length: 3}
]
rectangles.map((r) => {...r, area: r.side1Length * r.side2Length})
// ERROR!

The above code errors. This is because (r) => { looks like the start of a function to Javascript, but we meant to write a function which returns an object.

If we write the same code over multiple lines the problem becomes clearer:

const rectangles = [
  {side1Length: 1, side2Length: 2},
  {side1Length: 3, side2Length: 3}
]
rectangles.map((r) => {
  ...r, area: r.side1Length * r.side2Length // Not valid code!
})
// ERROR!

To solve this problem we must put our {} in an extra set of brackets ({}):

const rectangles = [
  {side1Length: 1, side2Length: 2},
  {side1Length: 3, side2Length: 3}
]
rectangles.map((r) => ({...r, area: r.side1Length * r.side2Length}))
// => [               ^                                           ^
//   { side1Length: 1, side2Length: 2, area: 2 },
//   { side1Length: 3, side2Length: 3, area: 9 }
// ]