We ended my previous post on Lenses and Partial-application with the following implementation for the lens function lookupGenerator
.
function lookupGenerator(...props) {
return obj =>
props
.reduce((o, p) =>
p in o ? o[p] : null, obj);
}
Since the previous version I have revised it to include some enhancements.
Recap on what lenses are used for
A lens is a function used to extract a value from an object/array given the properties/subscripts that defines its path. For example:
const testObject = {
alpha: [
{beta: '42', gamma: [ 'A', 'B', 'C']},
{beta: '666', gamma: [ 'a', 'b', 'c']}
]
};
const testLookup = lookupGenerator('alpha', 1, 'gamma', 2);
console.log(testLookup(testObject)); // 'c'
Where lenses really come into their owns is when they are applied within a method on an array of objects, as follows:
const betaLookup = lookupGenerator('beta');
testObject.alpha.forEach(obj => console.log(betaLookup(obj)));
// Output: 42, 666
The testObject
in the above examples is quite small and simple but imagine using this technique to pass an array of more complicated objects through methods such as sort
, map
or filter
.
So what is the limitation with the above implementation?
As discussed in my previous post, the above function employs partial-application to improve reuse. The function is called twice, once to provide the list of properties (and subscripts) used to navigate the object to find the required value. This returns a function that can be used several times by calling it with a compatible object (or array.)
There are a couple of ways to prescribe the route through the object to the required property. In the above example an array of property names and array subscripts were provided ('alpha', 1, 'gamma', 2)
but another way is to provide the route as a string as follows 'alpha[1].gamma[2]'
.
function lookupGenerator(...props) {
return obj =>
props
.join('.')
.split(/[\[\]\.]+/)
.filter(item => item !== '')
.reduce((o, p) =>
typeof o === 'object' && o != null &&
p in o ? o[p] : undefined, obj);
}
The above implementation can support either or both prescription approaches.
Input options
-------------
lookupGenerator('alpha', 1, 'gamma', 2); // arguments
lookupGenerator('alpha[1].gamma[2]'); // string
lookupGenerator('alpha[1]', 'gamma[2]'); // string arguments
How does it work?
First we join all the strings together to form a single string with a dot separating each sequence. We then separate each property name and array subscript using a Regular Expression (RegExp) match. For a discussion of the power of RegExp please read this post of mine.
The array of segments resulting from the split operation can produce empty matches that need to be filtered out before they are presented to the reduce method as before. Finally we need to guard against the reduce method failing to locate a property or finding a null mid process and throwing an exception.
I hope you have found this supplement informative but please provide any related questions you have in the discussion section below and I will be happy to try to provide an answer.
If partial application
is of interest, you might also be interested in my post on Currying.
Top comments (10)
It would be great if the function also accepted an array:
Hi Javarobit, There is no need to support an array because of the rest syntax.
In the examples you gave, if you really need to use an array, all you need to do is prefix the array with the spread syntax.
Alternatively, you could just use the elements of the array as separate arguments.
Thank you for your comments. Keep them coming.
Tracy
Great, otherwise I decided to use an expression like this:
You could probably simplify this using Array.flat.
You haven't written a single acticle on dev.to in 2024. Why?
Loads of reasons. Primarily because I have been too busy with work but also I write more for my own benefit than anyone. That said, I have always written posts so they are hopefully understandable by the widest possible readership. Thank you for taking the time to enquire and should you have any questions...
Why did you leave Medium.com? I couldn't find you there.
I left Medium for a couple of reasons.
I also found the feedback I received to be extremely negative on the whole. The entire experience became unpleasant so I stopped and moved to Dev.to.
However, Dev.to is starting to exhibit many of the same problems as Medium, made worse by the inclusion of AI-generated content, which is often incorrect.
This is another reason why my posts on Dev.to have reduced.
And you deleted all your articles on medium.com ?
The weren't many to delete so I moved them to Dev.to, where I have written many more. Before Medium I tried LinkedIn but became unhappy with that forum.