Mar 10 2020

Lodash FP

Although an avid user of Lodash, I have not really touched the Lodash FP module. I thought that it was not too different than the set of original functions. After noticing an annoying amount of repetition in my code, I decided to peek into it.

This is a couple of Redux selectors that grabs some values out of a store.

const _ = require('lodash')

const getSelectedIds = (state) => {
  return _.get(state, 'selectedIds')
}

const getSomeOtherValue = (state) => {
  return _.get(state, 'someOtherValue')
}

// ... About 20 more functions with this pattern

This patterns repeats on and on. You have some state argument being passed into a function that returns a value. After my adventure with Fluture, I’ve gotten the bug for compact code. Isn’t there some way to make this more compact? We can probably get rid of the repetitive fluff and just describe what we really want to accomplish.

If you have some experience in functional programming, you may know it’s common pattern to have curried functions. The last argument applied tends to be the most dynamic at runtime.

The red flag for me was when I discovered that Lodash FP reverses all of the arguments. This is key since usually the first argument to a Lodash function is the most dynamic at runtime (e.g. the data that needs to be processed).

With this being said, we can refactor the above code. Our goal is to create a function that takes some state argument.

const _ = require('lodash/fp')

const getSelectedIds = _.get('selectedIds')
const getSomeOtherValue = _.get('someOtherValue')

This refactor is only possible because Lodash FP both reverses the argument order as well as curries all functions. This also makes it obvious what we are selecting, and we can remove the verbose (state) argument that tends to litter select files.

Taking this one step further, let’s say that we want to write a function called getSelectedObjects that fetches the objects represented by the selectedIds.

const _ = require('lodash/fp')
import { createSelector } from 'reselect';
import { getObjectById } from './some_object';

const getSelectedIds = _.get('selectedIds')
const getSelectedObjects = createSelector(getObjectById, getSelectedIds, _.map)

This is function composition at its finest. We are describing the data flow instead of giving step-by-step instructions on what a program should do.

Lodash is quite forgiving with the currying. While (you probably should if you can) apply arguments one at a time, you can also call a curried function with several arguments all at once – it takes care of the partial application for you. You can find more about this in the _.curry examples. So fortunately our getSelectedObjects still works.

From here, we can build on top of getSelectedObjects and refine the data further. Maybe we want to sort by creation date. Maybe we want to filter by an attribute. Build another function!