Two common approaches are destructuring and conventional Lodash-like pick
/omit
implementation. The major practical difference between them is that destructuring requires a list of keys to be static, can't omit them, includes non-existent picked keys, i.e. it's inclusive. This may or not be desirable and cannot be changed for destructuring syntax.
Given:
var obj = { foo: 1, bar: 2, qux: 3 };
The expected result for regular picking of foo
, bar
, baz
keys:
{ foo: 1, bar: 2 }
The expected result for inclusive picking:
{ foo: 1, bar: 2, baz: undefined }
Destructuring
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 described in the question. Destructuring becomes more complicated if a key is non-alphanumeric, e.g. foo-bar
.
The upside is that it's performant solution that is natural to ES6.
The downside is that a list of keys is duplicated, 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.
IIFE
let subset = (({ foo, bar, baz }) => ({ foo, bar, baz }))(obj);
Temporary variables
let { foo, bar, baz } = obj;
let subset = { foo, bar, baz };
A list of strings
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, ['foo', someKey, ...moreKeys]
.
ECMAScript 2017 has Object.entries
and Array.prototype.includes
, ECMAScript 2019 has Object.fromEntries
, they can be polyfilled when needed.
One-liners
Considering that an object to pick contains extra keys, it's generally more efficient to iterate over keys from a list rather than object keys, and vice versa if keys need to be omitted.
Pick (ES5)
var subset = ['foo', 'bar', 'baz']
.reduce(function (obj2, key) {
if (key in obj) // line can be removed to make it inclusive
obj2[key] = obj[key];
return obj2;
}, {});
Omit (ES5)
var subset = Object.keys(obj)
.filter(function (key) {
return ['baz', 'qux'].indexOf(key) < 0;
})
.reduce(function (obj2, key) {
obj2[key] = obj[key];
return obj2;
}, {});
Pick (ES6)
let subset = ['foo', 'bar', 'baz']
.filter(key => key in obj) // line can be removed to make it inclusive
.reduce((obj2, key) => (obj2[key] = obj[key], obj2));
Omit (ES6)
let subset = Object.keys(obj)
.filter(key => ['baz', 'qux'].indexOf(key) < 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2));
Pick (ES2019)
let subset = Object.fromEntries(
['foo', 'bar', 'baz']
.filter(key => key in obj) // line can be removed to make it inclusive
.map(key => [key, obj[key]])
);
Omit (ES2019)
let subset = Object.fromEntries(
Object.entries(obj)
.filter(([key]) => !['baz', 'qux'].includes(key))
);
Reusable functions
One-liners can be represented as reusable helper functions similar to Lodash pick
or omit
, where a list of keys is passed through arguments, pick(obj, 'foo', 'bar', 'baz')
.
let pick = (obj, ...keys) => Object.fromEntries(
keys
.filter(key => key in obj)
.map(key => [key, obj[key]])
);
let inclusivePick = (obj, ...keys) => Object.fromEntries(
keys.map(key => [key, obj[key]])
);
let omit = (obj, ...keys) => Object.fromEntries(
Object.entries(obj)
.filter(([key]) => !keys.includes(key))
);
emo.slice
at first glance. – Bill Criswell