objects, undefined values, and lodash's `_.pickby` and `_.identity`

2019-05-01

 | 

~4 min read

 | 

675 words

Let’s start with the basics. I was in an oh-so-common situation recently where I was building a component that would visualize the details of an object.

Before I three things onto the DOM, however, I wanted to make sure that the detail was present — if it wasn’t, I wanted to leave it out.

My first attempt looked a bit like this.

function removeUndefined(object) {
  const newCollection = {}
  const createTupleCollection = Object.entries(object)
  const strippedCollection = createTupleCollection.filter((item) => item[1])
  strippedCollection.forEach((item) => (newCollection[item[0]] = item[1]))
  return newCollection
}

var object = { a: 1, b: undefined, c: 3, d: "alabama", e: 0 }
var dataToSend = removeUndefined(object)
console.log(dataToSend) // {a: 1, c: 3, d: "alabama"}*

The Object.entries creates a convenient tuple of [key, value] that I then use to in my filter to remove any where the second item (index 1) evaluates to false within the filter.

I then populate a new object, newCollection with the slimmed down array and return that. It works.

It’s also a lot of code and isn’t particularly clear in what / why it’s doing what it’s doing.

We use Lodash and a colleague refactored the code down to something much simpler - akin to

var object = { a: 1, b: undefined, c: 3, d: "alabama", e: 0 }
var dataToSend = _.pickBy(object, _.identity)
console.log(dataToSend) // {a: 1, c: 3, d: "alabama"}*

Progress! It’s certainly simpler and even semantically, it’s somewhat intuitive (even if I didn’t have all the details about how pickBy worked or what identity did). My dataToSend will be from picking the object by the identity.

So, the first thing I wanted to know was why did it work and I suspected it was the answer was _.identity . This was the predicate for pickBy and therefore the way pickBy decided what to include or not.

So, I started running some tests.

var object = { a: 1, b: undefined, c: 3, d: "alabama", e: 0 }

function myIdentity(x) {
  return x
}
function returnUndefined() {
  return undefined
}

var predicateIsNumber = _.pickBy(object, _.isNumber)
var predicateIdentity = _.pickBy(object, _.identity)
var predicateMyIdentity = _.pickBy(object, myIdentity)
var predicateDefault = _.pickBy(object) // _.identity is the default iteratee

console.log(`statement 1 --> `, {
  object,
  predicateIsNumber,
  predicateIdentity,
  predicateMyIdentity,
  predicateDefault,
})
// statement 1 -->
// object: {a: 1, b: undefined, c: 3, d: "alabama", e: 0}
// predicateDefault: {a: 1, c: 3, d: "alabama"}
// predicateIdentity: {a: 1, c: 3, d: "alabama"}
// predicateMyIdentity: {a: 1, c: 3, d: "alabama"}
// predicateIsNumber: {a: 1, c: 3}

console.log(`statement 2 --> `, _.identity(undefined) === undefined)
// statement 2 -->  true

console.log(`statement 3 --> `, _.identity(undefined), myIdentity(undefined))
// statement 3 -->  undefined undefined

console.log(
  `statement 4 --> calling myIdentity without an argument...`,
  myIdentity(),
)
// statement 4 --> calling myIdentity without an argument... undefined

console.log(`statement 5 --> `, returnUndefined())
// statement 5 --> undefined

I’ll be honest - not what I was expecting! It turns out undefined satisfies the identity principle! So does null by the way.

Okay - so if identity wasn’t why my undefined values were being dropped… it had to be pickBy.

And there it was, right there in the docs (my emphasis):

Creates an object composed of the object properties predicate returns truthy for. The predicate is invoked with two arguments: (value, key).

undefined is not truthy - so 'b' gets dropped. It’s also the case then that any of the other falsey values would also get dropped (which is around the time I added 'e':0 to the test).

Fortunately, all of my values are strings, since '0' is a valid answer that I would want to see!

Summary

It’s possible to accomplish the goal by writing your own utility function, but it’s not always pretty or particularly legible. Lodash can simplify your code base and improve readability - leading to faster development and better developer experience. The identity method returns the first argument passed to it. The pickBy passes each element of a collection to an iteratee and evaluates it for truthiness.



Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!