Imagine we have a complex object and would like to extract a few properties. For example, we would like to print first name and age from the following object
1 | var joe = { |
We could use the functional version of the popular lodash library that switches the order of arguments to something more sensible in my opinion.
1 | var _ = require('lodash-fp'); |
What if the input object has deep nesting and we would like to pick properties deep inside it? Imaging that the first name we need is nested inside an object
1 | var joe = { |
We can try using .
syntax in the property name, but that does not work with _.pick
yet
1 | console.log(_.pick(['name.first', 'age'], joe)); |
Remember, our goal is to produce a simple object with simple property names
1 | // fn is our "pick" function |
Extracting single deep property
Let us try a different approach. We can extract a single deep property using a feature introduced in the lodash@3.x.x version
1 | console.log(_.get(joe, 'name.first')); |
Except this method is not part of the lodash-fp
and requires the object to be specified first.
Another function that works very well in this situation is functional-pipeline.
One can extract a deep property quickly
1 | var fp = require('functional-pipeline'); |
Extracting multiple properties
Let us tackle the problem of extracting multiple deep properties using functional-pipeline
.
We can extract each property by applying the object at the end, but this forces the object
to exist when we declare the extraction.
1 | var simple = { |
This is good, but I really would like to declare the extraction mapping and then apply it to any object.
Let us NOT apply the function returned from fp('age')
to the object joe
, instead leaving it "free".
1 | var simple = { |
The object simple
is temporary: its properties are not the final values,
but the extractor functions like name: [Function]
.
Each extractor function needs be called with an actual object; only then it will return the extracted value.
The extractor functions can also be reused.
We need to write a new function that will go through each extractor property and actually fill it with values.
1 | function extract(schema, obj) { ... } |
We can write the extract
function ourselves: it is very small.
1 | function extract(picks, from) { |
I added the else
branch that copies the value in line // A
to allow adding new values.
1 | var simple = { |
Working with collections
We can partially apply extract
and use it to extract the deep properties over an array of objects
1 | var toSimple = extract.bind(null, simple); |
Compatible with other methods
You are not limited to the functional-pipeline method for getting the nested values. You can use other libraries to extract a property from an object, for example lodash/get, as long as it returns a function that will receive the actual object reference to work with.
1 | function _get(path) { |
Conclusion
I placed the extract
code into its own tested repo
bahmutov/functional-extract and made it available
on NPM and bower under name functional-extract
. Please use it and
let me know
if there are features or bugs.
- l33teral. This almost does what I need, but one cannot simplify the extracted keys. Instead the path values are used, for example
1 | var leet = require('l33teral'); |
I really did not want the keys to be the paths. The l33teral
also puts the object first, making
it difficult to work with collections.