DEV Community

Marcos Henrique
Marcos Henrique

Posted on

Optional handmade chaining

In my country (Brazil) we have a saying, who doesn't have a dog hunting with a cat.

I've been working on a project where we could not update the latest version of the node and there was no possibility to put the babel or even the experimental flag --harmony, because we had some enterprise restrictions to do something like that.

So it's time to use creativity

const optionalChainingByPath = (object, path) => {
  const pathSplitted = path.split('.')

  const [firstKey] = pathSplitted

 if (object[firstKey] == null || object[firstKey] ==='' ) { return null }
  if (typeof object[firstKey] === 'object') {
    pathSplitted.shift()
    return optionalChainningByPath(object[firstKey], pathSplitted.join('.'))
  }

  return object[firstKey]
}

Enter fullscreen mode Exit fullscreen mode

Usage:

const makeResponse = patient => ({
  name: optionalChainingByPath(patient, 'personalInformation.name'),
  gender: optionalChainingByPath(patient, 'personalInformation.gender'),
  cardNumber: optionalChainingByPath(patient, 'personalInformation.cardNumber')
})
Enter fullscreen mode Exit fullscreen mode

It's ok but unamused

Let's make this cool enough 戊

We'll use partial functions to transform this boring function into a fancy function

const optionalChainingByPath = object => path => {
  const pathSplitted = path.split('.')

  const [firstKey] = pathSplitted

  if (object[firstKey] == null || object[firstKey] === '') {
    return null
  }

  if (typeof object[firstKey] === 'object') {
    pathSplitted.shift()
    return optionalChainingByPath(object[firstKey], pathSplitted.join('.'))
  }

  return object[firstKey]
}
Enter fullscreen mode Exit fullscreen mode

Usage:

const makeResponse = patient => {
  return {
    name: optionalChaining('personalInformation.name'),
    gender: optionalChaining('personalInformation.gender'),
    cardNumber: optionalChaining('personalInformation.cardNumber')
  }
}
Enter fullscreen mode Exit fullscreen mode

Does sounds like a charm or doesn't?

Top comments (7)

Collapse
 
andrewbridge profile image
Andrew Bridge

Always fun to work through these problems as a learning experience, but here's another implementation, based on the lodash get method.

github.com/cedmax/youmightnotneed/...

The same GitHub repo has examples of set and has implementations.

Collapse
 
wakeupmh profile image
Marcos Henrique

Awesome tip fude, thank u for sharing

Collapse
 
felipperegazio profile image
Felippe Regazio • Edited

Really cool solution, it does look like a charm : )
You can also extract the object value in a functional style with:

const optionalChaining = (obj, str) => (obj && str) && (() => str.split('.').reduce((o, i) => o[i], obj))();
Enter fullscreen mode Exit fullscreen mode

and use like that:

const letters = { a: { b: 'c' } };

optionalChaining(letters, 'a'); // {b: "c"}
optionalChaining(letters, 'a.b'); // 'c'
optionalChaining(letters, 'a.b.e'); // undefined
optionalChaining(letters, 'z'); // undefined
Enter fullscreen mode Exit fullscreen mode

We can also extend the Object prototype to avoid some problems (dont know about how good or bad it could be in terms of design, but its possible):

Object.prototype.optionalChaining = function (str) {
  return str.split('.').reduce((o, i) => o[i], this);
}

const letters = { a: { b: 'c' } };

letters.optionalChaining('a'); // {b: "c"}
letters.optionalChaining('a.b'); // 'c'
letters.optionalChaining('a.b.e'); // undefined
letters.optionalChaining('z'); // undefined
Enter fullscreen mode Exit fullscreen mode
Collapse
 
wakeupmh profile image
Marcos Henrique

Clean and beauty, thanks bro

Collapse
 
olavoparno profile image
Olavo Parno

If Node's new stuff doesn't come to you, then move YOUR VERY SELF to Node's new stuff haha

Collapse
 
wakeupmh profile image
Marcos Henrique

Suhsiajaidjdi it's ALL folks xD

Collapse
 
ecyrbe profile image
ecyrbe • Edited

Here is another way based on proxy that that feels more native