Say I have an object:
elmo = {
color: \'red\',
annoying: true,
height: \'unknown\',
meta: { one: \'1\', two: \'2\'}
};
I want to m
Good-old Array.prototype.reduce
:
const selectable = {a: null, b: null};
const v = {a: true, b: 'yes', c: 4};
const r = Object.keys(selectable).reduce((a, b) => {
return (a[b] = v[b]), a;
}, {});
console.log(r);
this answer uses the magical comma-operator, also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
if you want to get really fancy, this is more compact:
const r = Object.keys(selectable).reduce((a, b) => (a[b] = v[b], a), {});
Putting it all together into a reusable function:
const getSelectable = function (selectable, original) {
return Object.keys(selectable).reduce((a, b) => (a[b] = original[b], a), {})
};
const r = getSelectable(selectable, v);
console.log(r);
I've got the same problem and solved it easily by using the following libs:
https://www.npmjs.com/package/object.pick
pick({a: 'a', b: 'b', c: 'c'}, ['a', 'b'])
//=> {a: 'a', b: 'b'}
https://www.npmjs.com/package/object.omit
omit({a: 'a', b: 'b', c: 'c'}, ['a', 'c'])
//=> { b: 'b' }
Adding my 2 cents to Ivan Nosov answer:
In my case I needed many keys to be 'sliced' out of the object so it's becoming ugly very fast and not a very dynamic solution:
const object = { a: 5, b: 6, c: 7, d: 8, aa: 5, bb: 6, cc: 7, dd: 8, aaa: 5, bbb: 6, ccc: 7, ffffd: 8, ab: 5, bc: 6, cd: 7, de: 8 };
const picked = (({ a, aa, aaa, ab, c, cc, ccc, cd }) => ({ a, aa, aaa, ab, c, cc, ccc, cd }))(object);
console.log(picked);
So here is a dynamic solution using eval:
const slice = (k, o) => eval(`(${k} => ${k})(o)`);
const object = { a: 5, b: 6, c: 7, d: 8, aa: 5, bb: 6, cc: 7, dd: 8, aaa: 5, bbb: 6, ccc: 7, ffffd: 8, ab: 5, bc: 6, cd: 7, de: 8 };
const sliceKeys = '({ a, aa, aaa, ab, c, cc, ccc, cd })';
console.log( slice(sliceKeys, object) );
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 }
Just another way...
var elmo = {
color: 'red',
annoying: true,
height: 'unknown',
meta: { one: '1', two: '2'}
}
var subset = [elmo].map(x => ({
color: x.color,
height: x.height
}))[0]
You can use this function with an array of Objects =)
function splice()
{
var ret = new Object();
for(i = 1; i < arguments.length; i++)
ret[arguments[i]] = arguments[0][arguments[i]];
return ret;
}
var answer = splice(elmo, "color", "height");