Destructuring syntax allows to destructure and recombine an object, with either function parameters or variables.
The limitation is that a list of keys is predefined, they cannot be listed as strings, as the question mentions. Destructuring becomes more complicated if a key is non-alphanumeric, e.g. foo_bar
.
The downside is that this requires to duplicate a list of keys, this results in verbose code in case a list is long. Since destructuring duplicates object literal syntax in this case, a list can be copied and pasted as is.
The upside is that it's performant solution that is natural to ES6.
let subset = (({ foo, bar }) => ({ foo, bar }))(obj); // dupe ({ foo, bar })
let { foo, bar } = obj;
let subset = { foo, bar }; // dupe { foo, bar }
Arbitrary list of picked keys consists of strings, as the question requires. This allows to not predefine them and use variables that contain key names, like pick(obj, 'foo', someKey, ...moreKeys)
.
A one-liner becomes shorter with each JS edition.
var subset = Object.keys(obj)
.filter(function (key) {
return ['foo', 'bar'].indexOf(key) >= 0;
})
.reduce(function (obj2, key) {
obj2[key] = obj[key];
return obj2;
}, {});
let subset = Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => Object.assign(obj2, { [key]: obj[key] }), {});
Or with comma operator:
let subset = Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});
ECMAScript 2017 has Object.entries
and Array.prototype.includes
, ECMAScript 2019 has Object.fromEntries
, they can be polyfilled when needed and make the task easier:
let subset = Object.fromEntries(
Object.entries(obj)
.filter(([key]) => ['foo', 'bar'].includes(key))
)
A one-liner can be rewritten as helper function similar to Lodash pick
or omit
where the list of keys is passed through arguments:
let pick = (obj, ...keys) => Object.fromEntries(
Object.entries(obj)
.filter(([key]) => keys.includes(key))
);
let subset = pick({ foo: 1, qux: 2 }, 'foo', 'bar'); // { foo: 1 }
The major difference between destructuring and conventional Lodash-like pick
function is that destructuring includes non-existent picked keys with undefined
value in a subset:
(({ foo, bar }) => ({ foo, bar }))({ foo: 1 }) // { foo: 1, bar: undefined }
This behaviour may or not be desirable. It cannot be changed for destructuring syntax.
While pick
can be changed to include missing keys by iterating a list of picked keys instead:
let inclusivePick = (obj, ...keys) => Object.fromEntries(
keys.map(key => [key, obj[key]])
);
let subset = inclusivePick({ foo: 1, qux: 2 }, 'foo', 'bar'); // { foo: 1, bar: undefined }