Short Answer
I looked extensively into the docs, and it would seem that currently in Lodash 4.17.15 (as of now 26th Jan 2021) no such functionality is directly implemented in the library.
Accordingly:
_.map(columns, row => _.pick(row, 'key'))
//Output, when using the columns collection I introduce below:
//[{key: "url"},
// {key: "user"},
// {}]
shall be considered the preferred solution.
If the key is not found:
- the key will simply not be attached to the object
- you will get back an empty object
Long Answer
I believe it may be worth noting what advantages and disadvantages may lay in the alternative approaches. Let's have a tour between them, considering the following collection, designed to show some drawbacks that may arise from each solution:
var columns = [
{ 0: 'pos_url', key:'url', width:20, type:'text' },
{ 1: 'pos_user', key:'user', width:24, type:'text' },
"key"
];
Try all the approaches explained here on Codepen if you like!
It may be a little messy, but I actually wrote a small codepen were you can make your hands dirty and try all it's explained here, feel free to try it:
https://codepen.io/jhack_jos/pen/gOwVReB
You have to open your browser console to see the console.log outputs.
1) Using the "project" function
If like me, you actually do not want to use mixins and mess with Lodash, you could also make use of the following function I created, called project
. It is inspired by Michael Fogus "Functional JavaScript", as he introduces a similar function in its talk about "Table-Like" Data and "Applicative Programming". The name comes from the unary operation called projection in relational algebra, which does something very similar.
with ES6
function project(...properties)
{
return function project(object, index, array)
{
return _.pick(object, ...properties);
};
}
without ES6
function project(/* properties here */)
{
var properties = arguments;
return function project(object, index, array)
{
return _.pick.bind(null, object).apply(null, properties);
};
}
Some examples of usage:
_.map(columns, project("key"));
//Output:
//[{key: "url"},
// {key: "user"},
// {}]
_.map(columns, project("key", "width"));
//Output:
//[{key: "url", width: 20},
// {key: "user", width: 24},
// {}]
_.map(columns, project("key", ["width", "type"]));
//Output:
//[{key: "url", width: 20, type: "text"},
// {key: "user", width: 24, type: "text"},
// {}]
How fast are the different implementations of project
?
- the ES6 implementation is always the fastest
- without ES6, on V8 the bind + apply combination is the fastest implementation I found. On SpiderMonkey, even if by a small margin, using Array.prototype.concat is faster. You can find such implementation in the link below.
- link to the tests on jsbench.me, also containing other possible implementations of
project
: https://jsbench.me/2gkkfl9pw4
If the key is not found:
- the key will simply not be attached to the object
- you will get back an empty object
2) The _.mapPick approach
I actually really like the mixin approach used by @pery mimon in the question. It is also possible to use _.chain
with it, which makes the code very readable. Just keep in mind the possibility of clashing with future Lodash updates. Unfortunately, the algorithm as it is does not behave as one may expect when it is thrown a list of properties as arguments (it ignores all but the first argument). I may suggest the following improvement:
with ES6
_.mixin({
mapPick: function (objs, ...keys) {
return _.map(objs, function (obj) {
return _.pick(obj, ...keys);
})
}
});
without ES6
_.mixin({
mapPick: function (objs) {
var properties = arguments;
return _.map(objs, function (obj) {
return _.pick.bind(null, obj).apply(null, properties);
})
}
});
Thanks to this we can now do:
_.mapPick(columns, "key", "width")
//Output now:
//[{key: "url", width: 20}
// {key: "user", width: 24}
// {}]
//Output of the original mixin from the question:
//[{key: "url"}
// {key: "user"}
// {}]
If the key is not found:
- the key will simply not be attached to the object
- you will get back an empty object
3) The _.partialRight approach
This approach is not recommended, as it may reveal to be tricky if incorrectly applied in edge case scenarios. Lodash docs states _.partialRight
does the following:
This method is like _.partial except that partially applied arguments
are appended to the arguments it receives.
This means that _.partialRight(func, arg1, arg2) (arg3, arg4)
resolves into func(arg3, arg4, arg1, arg2)
.
The wrong way to do it
As such, the following code:
_.map(columns, _.partialRight(_.pick, 'key'));
Resolves into:
_.map(columns, (row, index, columns) => _.pick(row, index, columns, 'key'));
//Output:
//[{0: "pos_url", key: "url"},
// {1: "pos_user", key: "user"},
// {2: "y"}]
If the key is not found:
- the key will simply not be attached to the object
- you will get back an empty object
The right way to do it
You should therefore use _.unary
to restrict the arguments passed down to _.pick
to just one. Code is as follows:
_.map(columns, _.unary(_.partialRight(_.pick, 'key')));
This resolves to:
_.map(columns, (row, index, columns) => _.pick(row, 'key'));
//Output:
//[{key: "url"},
// {key: "user"},
// {}]
If the key is not found:
- the key will simply not be attached to the object
- you will get back an empty object
4) Destructuring and arrow functions
You may expect this approach to cover everything and be vanilla, but you may find it slightly inflexible in edge cases. Let's have a look at it:
columns.map(({key}) => ({key}))
//Output:
//[{key: "url"},
// {key: "user"},
// {key: undefined}]
A few relevant facts:
- you cannot pick numerical keys
- you cannot pick keys that contain spaces
If the key is not found:
- you will get a key with value "undefined"
- you will never get an empty object
_.keys
will still return the key that was not found
5) Extracting values without keys
This one is an extra, and does not comply with the question. As another answer pointed out, in case you do not need the keys, you may just extract the values with the _.map
shortcut (previously known as _.pluck
):
_.map(columns, "key")
//Output:
//["url", "user", undefined]
There are a lot of interesting things to know about this way of doing it, as it implicitly makes use of the _.iteratee function, which enables us to make such a brilliant use of _.get with _.map
.
If the key is not found:
- undefined will be used as a value
- you will get an array of the same length as the original, but filled with "undefined" values