768

Say I have an object:

elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
};

I want to make a new object with a subset of its properties.

 // pseudo code
 subset = elmo.slice('color', 'height')

 //=> { color: 'red', height: 'unknown' }

How may I achieve this?

frederj
  • 1,483
  • 9
  • 20
Christian Schlensker
  • 21,708
  • 19
  • 73
  • 121

36 Answers36

1160

Using Object Destructuring and Property Shorthand

const object = { a: 5, b: 6, c: 7  };
const picked = (({ a, c }) => ({ a, c }))(object);

console.log(picked); // { a: 5, c: 7 }

From Philipp Kewisch:

This is really just an anonymous function being called instantly. All of this can be found on the Destructuring Assignment page on MDN. Here is an expanded form

let unwrap = ({a, c}) => ({a, c});

let unwrap2 = function({a, c}) { return { a, c }; };

let picked = unwrap({ a: 5, b: 6, c: 7 });

let picked2 = unwrap2({a: 5, b: 6, c: 7})

console.log(picked)
console.log(picked2)
ruffin
  • 16,507
  • 9
  • 88
  • 138
Ivan Nosov
  • 15,395
  • 2
  • 16
  • 14
  • 65
    How did you learn about how to do this? Nowhere in any docs or articles I've seen (including MDN) does it show the arrow syntax being used in Object Destructuring. This is very nice to know. – papiro Jan 03 '17 at 05:25
  • 74
    This is really just an anonymous function being called instantly. All of this can be found on the Destructuring Assignment page on MDN. Here is an expanded form: `let unwrap = ({a, c}) => ({a, c}); let unwrap2 = function({a, c}) { return { a, c }; }; let picked = unwrap({ a: 5, b: 6, c: 7 });` – Philipp Kewisch Jan 27 '17 at 11:41
  • 16
    is there a way to do it dynamically with the spread operator? – Tomas Ramirez Sarduy Jun 13 '17 at 17:24
  • Yes, it works. Just a comment here. You could use `const` instead of `let` if you won't reassign the objects. – Anastasis Jun 16 '17 at 14:16
  • How do you generalise this to any number of choices, e.g. a; a,b; a,b,c; b,c ? Assume we have the attribute names as an array of strings – Randhir Rawatlal Jun 29 '17 at 05:27
  • 1
    Not really a black magic. its just an immediately invoked anonymous (arrow) function that accepts an object, destructs its properties and then returning them. the thing is though, i couldn't think of it my self so i found my self here. nice one! :) I wonder if we can pass them somehow without repeating their names. – Sagiv b.g Jul 25 '17 at 20:23
  • Is there a way to pass an array of properties I need to select? – Vedmant Aug 07 '17 at 11:49
  • 9
    @TomSarduy you could use rest if you want to specify which props to remove, e.g. `const { b, ...picked } = object` would create `picked` as `{ a: 5, c: 7 }`. You've specified simply to remove b. Your eslint will probably be annoyed at you for declaring a var that you're not using, though. – Josh from Qaribou Aug 28 '17 at 02:13
  • @RandhirRawatlal if you have the attribute names in an array of strings, Array#reduce is the obvious choice, as in my answer – Josh from Qaribou Aug 28 '17 at 02:19
  • 3
    I do really like this, from a looks cool point of view (it looks really cool), but is it more functional than `const picked = {a: object.a, b: object.b}`, which I think is easier to read, and shorter. – JonathanPeel Jul 06 '18 at 06:24
  • 61
    A disadvantage here is that you need to fully type out the series of attribute names twice. That could be quite an issue in cases where many attributes need to be picked. – Gershom Maes Oct 09 '18 at 19:29
  • For some symbol field, how to do that? For example, the following will not pass the compiler: `({event, method, @target, @server}) => ({event, method, @target, @server})` – Jeff Tian Mar 14 '19 at 09:48
  • OK, for the symbol field, can handle it by ({event, method, '@target': target, '@server': server}) => ({event, method, '@target': target, '@server': server}) – Jeff Tian Mar 14 '19 at 10:15
  • 2
    @GershomMaes u r right, we need to type twice for each attribute which is a pain. Any idea we can shorten the code? – zhuhang.jasper Apr 09 '19 at 03:09
  • 2
    Now, how can you do that without repeating yourself? – Leonardo Raele Oct 11 '19 at 18:51
  • 2
    This was a good answer when it was written, but Estus Flask's answer below is better now, with all the improvements that have been made to JS. – Vinay Pai Dec 06 '19 at 21:33
  • 1
    Here's a horrendous but comparable way to reimplement this approach to avoid the dreaded duplication: `const pick = (o, ...p) => eval(\`(({${p.a=p.join(',')}})=>({${p.a}}))(o)\`)` then `pick(object, 'a', 'b')` – Brett Zamir Feb 13 '20 at 14:20
  • Very cool! Is it possible to get the same thing from properties defined in interface. I mean, I have an interface Iac = {a: number; c: number}. I want to get a and c from object but using the interface (I don't know the name of the properties)? – IsraGab Mar 04 '20 at 22:26
  • What if you want to pick using an array of keys like pick – Batman Jul 01 '20 at 21:20
  • What if the objects has keys which have dashes? – Watson May 30 '21 at 02:03
  • @Watson Variables need to be renamed to proper names and back, `const picked = (({ 'a-b': aB, c }) => ({ 'a-b': aB, c }))(object)` – Estus Flask Nov 15 '21 at 21:59
  • 1
    I'm hoping ES introduces a way to do this using destructuring. This is a common syntax limitation in my opinion. Here's one proposal, though i don't know if there's anything similar in the proposals making any progress. https://github.com/rtm/js-pick-notation. It would be nice to be able to `obj1.{p1, p2} = obj2` or `obj2 = {a, b} from obj1` – Gisheri Jan 25 '22 at 22:27
  • Why exactly does JS need two parens when assigning the results of the IIFE to a variable? I get it, but wondering if there's actual terminology for what this is called – user2402616 Mar 08 '22 at 14:33
  • this article [here](https://medium.com/@captaindaylight/get-a-subset-of-an-object-9896148b9c72) by Paul seems to explain this in great detail, amazing answer. For someone who is looking for more details, It will be helpfull – ibrez Jun 26 '22 at 06:49
  • This doesn't answer the question. What if the properties to be picked aren't known before runtime? If I asked "how to get max value in array e.g. `[5, 2, 9, 0 ]`" and someone replied with the single character `9`, I would downvote that too. – Gershom Maes Jan 27 '23 at 17:48
334

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-bar': 1, bar: 2, qux: 3 };

The expected result for regular picking of foo-bar, bar, baz keys:

{ 'foo-bar': 1, bar: 2 }

The expected result for inclusive picking:

{ 'foo-bar': 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

const subset = (({ 'foo-bar': foo, bar, baz }) => ({ 'foo-bar': foo, bar, baz }))(obj);

Temporary variables

Can cause the collision of variable names in current scope:

const { 'foo-bar': foo, bar, baz } = obj;
const subset = { 'foo-bar': foo, bar, baz };

Block-level scope can be used to avoid this:

let subset;
{
  const { 'foo-bar': foo, bar, baz } = obj;
  subset = { 'foo-bar': 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-bar', 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', '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)

const subset = ['foo-bar', '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)

const subset = Object.keys(obj)
.filter(key => ['baz', 'qux'].indexOf(key) < 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});

Pick (ES2019)

const subset = Object.fromEntries(
  ['foo-bar', 'bar', 'baz']
  .filter(key => key in obj) // line can be removed to make it inclusive
  .map(key => [key, obj[key]])
);

Omit (ES2019)

const 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', 'bar', 'baz').

JavaScript

const pick = (obj, ...keys) => Object.fromEntries(
  keys
  .filter(key => key in obj)
  .map(key => [key, obj[key]])
);

const inclusivePick = (obj, ...keys) => Object.fromEntries(
  keys.map(key => [key, obj[key]])
);

const omit = (obj, ...keys) => Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => !keys.includes(key))
);

TypeScript

Credit goes to @Claude.

const pick = <T extends {}, K extends keyof T>(obj: T, ...keys: K[]) => (
  Object.fromEntries(
    keys
    .filter(key => key in obj)
    .map(key => [key, obj[key]])
  ) as Pick<T, K>
);

const inclusivePick = <T extends {}, K extends (string | number | symbol)>(
  obj: T, ...keys: K[]
) => (
  Object.fromEntries(
    keys
    .map(key => [key, obj[key as unknown as keyof T]])
  ) as {[key in K]: key extends keyof T ? T[key] : undefined}
)

const omit = <T extends {}, K extends keyof T>(
  obj: T, ...keys: K[]
) =>(
  Object.fromEntries(
    Object.entries(obj)
    .filter(([key]) => !keys.includes(key as K))
  ) as Omit<T, K>
)
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 31
    What a shame that this answer is so recent and thus isn't getting the exposure it deserves. IMO it should be the accepted answer for completeness, simplicity, versatility and, just-workiness. I'll keep the ES6 version in my most-useful snippet library. – VanAlbert Jul 07 '19 at 13:27
  • 4
    I'm not a fan of the `.indexOf`/`.includes` solutions -- that's doing an `O(keys)` lookup on every iteration = `O(entries*keys)`. Better to flip the logic around and just iterate the keys, then you get `O(keys)` total. – mpen Sep 29 '20 at 22:52
  • 1
    @mpen This concern can be considered premature optimization because in most real-word situations it doesn't affect the performance at all, so just pick one that's easy to digest. And for well-timed optimization one may find that Lodash isn't that fast (it really isn't), and using array methods to iterate over objects shouldn't be the first choice either. Any way, I usually find myself using iteration over a list myself for pick and iteration over object keys for omit, and updated the post to reflect this. – Estus Flask Jun 11 '21 at 18:05
  • 3
    @EstusFlask It may be premature, but when there's a faster big-O sol'n that takes the same # of lines to implement, I prefer that. Might be fine if you're inlining this code, but as soon as you turn it into a utility function it should be optimized IMO, because you don't know where it will be used. – mpen Jun 11 '21 at 19:52
  • @mpen Btw, it also can be the opposite, with a lot of listed keys missing in an object, in this case iterating over a list will be more redundant, so it always depends. – Estus Flask Jun 11 '21 at 20:11
  • I would prefer `.filter(key => obj.hasOwnProperty(key))` over `.filter(key => key in obj)`. – Gershom Maes Nov 20 '21 at 21:09
  • @GershomMaes This depends on your intentions. But generally I'd expect prototype inheritance to be respected when a key is picked. Because it will in case of destructuring and accessing a value by key. – Estus Flask Nov 20 '21 at 22:25
  • @EstusFlask that's true, I suppose it's a bit of a philosophical preference. I personally figure there's no use picking anything but consumer data out of an object, which makes winding up with a `toString` function reference (for example) more of a liability than anything – Gershom Maes Nov 22 '21 at 04:42
  • I was sure ES6 had a better syntax. What a shame that we either have to wrap keys in quotes, or type property names twice. IMO all this destructuring syntax is useless against the good old structured sub-objects. – Brian Cannard Jan 16 '22 at 21:58
  • 1
    @BrianCannard It's good for what it's commonly used, just not for re-structuring like shown in the post. I doubt I ever used quoted destructured keys in wild life. The most inconvenient thing about the syntax is that it's impossible to debug the whole param in destructured arrow params without changing code, because there's no `arguments` – Estus Flask Mar 08 '22 at 07:03
  • @EstusFlask I agree. The best use of destructuring is to pass arguments by name rather than by order. By the "structured sub-objects" I mean mixins, classes with diamond inheritance (very useful for interfaces), which JavaScript's prototype-based inheritance has always lacked. The need is obviously there. And this lack of ability to compose complex objects as intersections of hyperslices is quite limiting at times... – Brian Cannard Mar 11 '22 at 05:04
  • ES6 versions need an initial value of `{}` or `new Object()`. – Jacktose Mar 20 '22 at 09:08
  • 1
    @Jacktose Indeed, lost in edits, thanks for noticing – Estus Flask Mar 20 '22 at 09:17
  • 1
    I converted the functions into typescript: [gist](https://gist.github.com/reinhrst/ae00182995771206ab76ad6dc613933f). Note that pick and omit, I only allow existing keys (meaning in pick you can include optional types); it's not that hard to convert the functions into allowing all possible keys – Claude Feb 20 '23 at 09:00
  • @Claude Thanks for the work. Makes sense, if the object type doesn't contain these keys, it likely was typed the wrong way. Added to the post – Estus Flask Feb 20 '23 at 09:20
  • The one-liners are bizarrely broken up onto multiple lines each. – anothermh Mar 23 '23 at 17:45
255

I suggest taking a look at Lodash; it has a lot of great utility functions.

For example pick() would be exactly what you seek:

var subset = _.pick(elmo, ['color', 'height']);

fiddle

Ygg
  • 3,798
  • 1
  • 17
  • 23
  • 2
    same for underscore.js – Dan Nov 28 '16 at 04:52
  • 2
    Is there any function to exclude only certain fields instead of selecting? so I have about 50 fields in my json and want everything except just 2 fields. – Shrikant Prabhu Jul 03 '18 at 01:01
  • 24
    yep! you can use `_.omit(elmo, ['voice'])` to return everything but `voice` – xavdid Jul 04 '18 at 01:03
  • 6
    what I don't like about this approach is you're putting the field names in quotes so it's susceptible to typos, common refactorings like renaming a property in your IDE won't pick it up, etc.etc. – Andy May 12 '20 at 18:03
  • 2
    You don't need underscore/lodash to accomplish that. I believe vanilla js solutions are better. – iedmrc Jul 28 '20 at 08:57
  • 1
    @ShrikantPrabhu https://codeburst.io/use-es2015-object-rest-operator-to-omit-properties-38a3ecffe90 – Trevor Sep 28 '20 at 21:37
  • @Andy Typescript comes to rescue. It's possible to guarantee type safety for this case. – Estus Flask Nov 15 '21 at 22:09
  • @ShrikantPrabhu you csn use _.omit() to return a new copy of the object, devoid of the properties you specify. – SherylHohman Nov 19 '21 at 17:46
  • in some old version of loadash this doesn't work. and in my case this will work sometimes when it feels good i guess. most inconsistent library ever. – Vikas Acharya Jan 28 '23 at 08:47
198

If you are using ES6 there is a very concise way to do this using destructuring. Destructuring allows you to easily add on to objects using a spread, but it also allows you to make subset objects in the same way.

const object = {
  a: 'a',
  b: 'b',
  c: 'c',
  d: 'd',
}

// Remove "c" and "d" fields from original object:
const {c, d, ...partialObject} = object;
const subset = {c, d};

console.log(partialObject) // => { a: 'a', b: 'b'}
console.log(subset) // => { c: 'c', d: 'd'};
Nate
  • 18,752
  • 8
  • 48
  • 54
Lauren
  • 2,557
  • 1
  • 14
  • 16
  • 18
    this only works to remove a field, not to select a known subset? potentially an infinite number of unknown fields to remove, but it might be what some people are looking for – Alexander Mills May 26 '18 at 02:10
  • 2
    True, but it can remove several known fields which can then be reassigned to a new object so it still feels relevant to this question. Added to the answer to further illustrate. – Lauren May 27 '18 at 19:16
  • 1
    This is essentially the same as what is in as [@Ivan Nosov's answer](https://stackoverflow.com/a/39333479/327074), albeit it's explained in a more understandable way here – icc97 Sep 10 '18 at 12:53
  • 1
    @icc97 I think this is the opposite of Ivan's answer. Ivan is specifying a subset within an IIFE, whereas this answer is using spread to essentially destructure all except. That is, one is a blacklist, the other is a whitelist. This could matter a lot if you were using Lauren's answer to destructure an object containing sensitive data (e.g., a user session) – Nick Bull Jan 07 '21 at 19:09
  • Drat. Accidentally downvoted :-(. I clicked the down button again to undo (as hints often specify), but it instead locked in my vote! SO drives me bezerk on this. Sometimes it works as expected, other times it does the antethsis of what I want! It's been unpredictable for YEARS. I dunno if it depends on browser/OS/app(now depricated)/mobile/touch, or what!? Ocasionally I make a frivilous edit to be able to change vote. Even then I dunno how it will react. Will an up take back to neutral, or become an upvote? Here upvote would be fine. Elsewhere I might require neutral. But How? Needs fixing. – SherylHohman Nov 19 '21 at 18:07
112

While it's a bit more verbose, you can accomplish what everyone else was recommending underscore/lodash for 2 years ago, by using Array.prototype.reduce.

var subset = ['color', 'height'].reduce(function(o, k) { o[k] = elmo[k]; return o; }, {});

This approach solves it from the other side: rather than take an object and pass property names to it to extract, take an array of property names and reduce them into a new object.

While it's more verbose in the simplest case, a callback here is pretty handy, since you can easily meet some common requirements, e.g. change the 'color' property to 'colour' on the new object, flatten arrays, etc. -- any of the things you need to do when receiving an object from one service/library and building a new object needed somewhere else. While underscore/lodash are excellent, well-implemented libs, this is my preferred approach for less vendor-reliance, and a simpler, more consistent approach when my subset-building logic gets more complex.

edit: es7 version of the same:

const subset = ['color', 'height'].reduce((a, e) => (a[e] = elmo[e], a), {});

edit: A nice example for currying, too! Have a 'pick' function return another function.

const pick = (...props) => o => props.reduce((a, e) => ({ ...a, [e]: o[e] }), {});

The above is pretty close to the other method, except it lets you build a 'picker' on the fly. e.g.

pick('color', 'height')(elmo);

What's especially neat about this approach, is you can easily pass in the chosen 'picks' into anything that takes a function, e.g. Array#map:

[elmo, grover, bigBird].map(pick('color', 'height'));
// [
//   { color: 'red', height: 'short' },
//   { color: 'blue', height: 'medium' },
//   { color: 'yellow', height: 'tall' },
// ]
Josh from Qaribou
  • 6,776
  • 2
  • 23
  • 21
  • 3
    es6 makes it possible for this to be even cleaner via arrow functions, and Object.assign's return (since assigning to an object property returns the property value, but Object.assign returns the object.) – Josh from Qaribou Mar 07 '16 at 12:59
  • Another es6 note: you'll very seldom need to do this at all anymore, since you typically just destructure assignment or args. e.g. `function showToy({ color, height }) {` would put only what you need in scope. The `reduce` approach mainly makes sense when you're simplifying objects for serialization. – Josh from Qaribou Apr 08 '16 at 16:43
  • 9
    That ES6 version is less performant, because it makes a copy of all the properties with each iteration. It makes an O(n) operation into O(n^2). An ES6 equivalent of your first code block would be `const pick = (obj, props) => props.reduce((a, e) => (a[e] = obj[e], a), {});` – 4castle Feb 24 '17 at 18:52
  • @4castle yep good call - no sense iterating so much. I like the comma syntax - better than a bunch of returns. – Josh from Qaribou Feb 24 '17 at 20:58
  • ```const elmo = { a: 5, b: 6, c: 7 }; const subset = ['a', 'b'].reduce((a, e) => Object.assign(a, { [e]: elmo[e] }), {});``` or if you use Object spread `const subset = ['a', 'b'].reduce((a, e) => ({ ...a, { [e]: elmo[e] } }), {});` – Shevchenko Viktor Jul 11 '17 at 10:53
  • 1
    @ShevchenkoViktor I'd actually used that approach in my original es6 version, but changed it after @4castle 's comment. I think the spread is more clear, but it's a huge difference for larger objects in code that could easily be on a bottleneck (eg delaying rendering data returned from `fetch`), so I'd recommend adding a comment explaining the comma operator use. – Josh from Qaribou Jul 11 '17 at 11:30
  • @JoshfromQaribou, just verified this on jsperf - you are correct. Simple property assignment `a['e'] = b['e']` works in 99% faster than `Object.assign(a, {e: b['e']})`. So comma operator is much useful here, using it in a return statement makes only last sub-statement to be returned, but all previous to be evaluated. `x=1; return (x=x+1,x)` will return 2 – Shevchenko Viktor Jul 13 '17 at 12:40
  • thats the one Im using for the fact this is dynamic. Thanks. – Andre Elrico Feb 26 '20 at 13:13
60

One more solution:

var subset = {
   color: elmo.color,
   height: elmo.height 
}

This looks far more readable to me than pretty much any answer so far, but maybe that's just me!

Evert
  • 93,428
  • 18
  • 118
  • 189
  • 23
    I prefer being productive over being fancy but confusing code, and in real life software engineering this is far the most readable and maintainable solution. – Janos Nov 08 '18 at 15:27
  • 3
    Yes, however, to me, one upside of using destructuring and shorthand notation is that it's less error prone. If I'd had a penny for every time I've mistakingly copy & pasted code to end up with `subset = {color: elmo.color, height: elmo.color}`, I'd have had at least a ... well, a dime perhaps. – JHH Jan 10 '20 at 07:57
  • I wouldn't call the destructuring shorthand less error prone as it's not D.R.Y. – gman Apr 07 '20 at 04:46
  • 2
    I'd have to agree. Without polluting the context with unwanted variables, this is by far the most readable solution. The rest look way too confusing. I prefer to understand my code the second I look at it. – Andrew Apr 18 '20 at 02:11
  • 1
    I tend to agree with this... The destructuring + anonymous function is just as verbose. If you are doing this often, then one of the more declarative "utility" functions would be worth using – NSjonas Apr 07 '21 at 05:05
  • I very much agree with this. I also felt like there must be some kind of intuitive restructuring way to do this but alas. If this was valid it would be my favorite way. let subset = { set.a, set.b }. Or something like subset = set[a, b], and then things like subset = { ...set[a, b], c : 3 }, Maybe the next js spec with have something fancy for us – Kyle Zimmer Jun 22 '21 at 16:39
  • This should be the top answer. There's no reason someone should write a wiki article for how to get the proptery of an object. – Ian Shirley Mar 22 '22 at 22:21
  • This is a great counterpoint to those suggesting the IIFE approach, but it doesn't answer the question, which asks about *generally* taking a subset of an `Object`. What if the subset keys are received as an array, whose contents are only known at runtime? – Gershom Maes Jul 20 '22 at 16:48
  • I chose this after considering many other approaches (most of them fancier or more modern), but this is it. Intuitive, like a primitive constructor assignment, and pretty shorthand for a few properties. – dakab Mar 13 '23 at 17:46
60

I am adding this answer because none of the answer used Comma operator.

It's very easy with destructuring assignment and , operator

const object = { a: 5, b: 6, c: 7  };
const picked = ({a,c} = object, {a,c})

console.log(picked);
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
  • 1
    interestingly the code snippet works but in Node I get an error "object is not defined" – ekkis Feb 20 '19 at 20:28
  • @ekkis https://repl.it/@VivekJain1/PossibleFortunateMonotone i don't see any problem. – Code Maniac Feb 21 '19 at 04:56
  • 2
    my bad, I don't know what I did before that it didn't work but it does seem to work – ekkis Feb 22 '19 at 06:25
  • 3
    That's the best expression, when it come to destructuring – Mohamed Allal Apr 28 '19 at 17:36
  • 3
    this solution is clever, but it doesn't work in strict mode (i.e., `'use strict'`). I get a `ReferenceError: a is not defined`. – kimbaudi Jul 18 '19 at 06:55
  • 20
    Note that this approach pollutes the current scope with two variables `a` and `c` - be careful not to randomly overwrite local or global vars depending on the context. (The accepted answer avoids this issue by using two local variables in an inline function, which falls out of scope after immediate execution.) – mindplay.dk Aug 15 '19 at 12:12
  • 5
    The namespace pollution makes this completely impractical. It's extremely common to already have variables in scope that match object properties, that's why the prop shorthand + destructuring exists.Very likely you'll have height or color already defined like in the original example. – Josh from Qaribou Feb 13 '20 at 11:16
  • 3
    A correct way to do this is to declare temp vars, `let a, c; const picked = ({a,c} = object, {a,c})`. Unfortunately, comma operator wasn't suggested in other answers for a very good reason, it doesn't make this any easier than `const {a, c} = object; const picked = {a,c}`. – Estus Flask May 12 '20 at 08:17
  • 1
    This is the cleanest answer I've seen – tettoffensive Jun 11 '20 at 22:46
55

There is nothing like that built-in to the core library, but you can use object destructuring to do it...

const {color, height} = sourceObject;
const newObject = {color, height};

You could also write a utility function do it...

const cloneAndPluck = function(sourceObject, keys) {
    const newObject = {};
    keys.forEach((obj, key) => { newObject[key] = sourceObject[key]; });
    return newObject;
};

const subset = cloneAndPluck(elmo, ["color", "height"]);

Libraries such as Lodash also have _.pick().

alex
  • 479,566
  • 201
  • 878
  • 984
  • 3
    great, i just had to change the forEach to: keys.forEach(key => { newObject[key] = sourceObject[key]; });. Please, update the comment if this makes sense. – kandan Feb 25 '19 at 12:54
36

TypeScript solution:

function pick<T extends object, U extends keyof T>(
  obj: T,
  paths: Array<U>
): Pick<T, U> {
  const ret = Object.create(null);
  for (const k of paths) {
    ret[k] = obj[k];
  }
  return ret;
}

The typing information even allows for auto-completion:

Credit to DefinitelyTyped for U extends keyof T trick!

TypeScript Playground

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • @Nuthinking Are you using VS Code? – mpen May 27 '20 at 17:46
  • @Nuthinking And you put that in a .ts file? That should work. I just tried it again – mpen May 28 '20 at 17:09
  • 1
    came here looking for this, nice one! – Arkadiy Kukarkin Sep 29 '20 at 22:00
  • 2
    actually, I would suggest one change: `function pick(obj: T, paths: Array): Pick` that `Pick` will correctly type the returned object, which is inferred as `any` – Arkadiy Kukarkin Oct 02 '20 at 19:48
  • @ArkadiyKukarkin Excellent suggestion; amended! – mpen Oct 03 '20 at 01:27
  • 1
    Beautiful solution! I have several attributes to pick and the object destructuring trick looks awfully bad when used with 4 or 5 attributes. – seniorpreacher Feb 22 '21 at 08:27
  • @mpen how would you modify the example to be able to pass `const allowedKeys:string[] = ["a","b"]`? This would help me answer https://stackoverflow.com/questions/75047175/how-do-you-remove-properties-from-all-objects-of-an-array-in-typescript-from-a-w – Victor Jan 08 '23 at 12:29
  • @Victor You should change the type of `allowedKeys` to `Array`. Or if you really don't want to do that you can modify my code to `U extends string` or `U extends PropertyKey`, but the resulting object will contain undefined values if they're not in the object. – mpen Jan 09 '23 at 01:18
  • Object.create(null) seems to be a result of overthinking, can cause corner problems when prototype chain matters without a justification. `const ret = {} as Pick` is safer – Estus Flask Apr 02 '23 at 16:54
  • @EstusFlask I guess it depends on what you value. I prefer not to add dependencies on and unnecessary traversal of the prototype chain. – mpen Apr 03 '23 at 02:23
  • @mpen Premature optimization is the root of all evil, hah. The benefit is nonexistent for an arbitrary object in code, unless proven otherwise. But this adds a possibility of bugs, this happened to me at least once in JS – Estus Flask Apr 03 '23 at 02:40
24

I want to mention that very good curation here:

pick-es2019.js

Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => ['whitelisted', 'keys'].includes(key))
);

pick-es2017.js

Object.entries(obj)
.filter(([key]) => ['whitelisted', 'keys'].includes(key))
.reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});

pick-es2015.js

Object.keys(obj)
.filter((key) => ['whitelisted', 'keys'].indexOf(key) >= 0)
.reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})

omit-es2019.js

Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => !['blacklisted', 'keys'].includes(key))
);

omit-es2017.js

Object.entries(obj)
.filter(([key]) => !['blacklisted', 'keys'].includes(key))
.reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});

omit-es2015.js

Object.keys(obj)
.filter((key) => ['blacklisted', 'keys'].indexOf(key) < 0)
.reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})
iedmrc
  • 742
  • 2
  • 8
  • 21
13

You can use Lodash also.

var subset = _.pick(elmo ,'color', 'height');

Complementing, let's say you have an array of "elmo"s :

elmos = [{ 
      color: 'red',
      annoying: true,
      height: 'unknown',
      meta: { one: '1', two: '2'}
    },{ 
      color: 'blue',
      annoying: true,
      height: 'known',
      meta: { one: '1', two: '2'}
    },{ 
      color: 'yellow',
      annoying: false,
      height: 'unknown',
      meta: { one: '1', two: '2'}
    }
];

If you want the same behavior, using lodash, you would just:

var subsets = _.map(elmos, function(elm) { return _.pick(elm, 'color', 'height'); });
Arthur Alvim
  • 1,044
  • 12
  • 23
10

Destructuring into dynamically named variables is impossible in JavaScript as discussed in this question.

To set keys dynamically, you can use reduce function without mutating object as follows:

const getSubset = (obj, ...keys) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});

const elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
}

const subset = getSubset(elmo, 'color', 'annoying')
console.log(subset)

Should note that you're creating a new object on every iteration though instead of updating a single clone. – mpen

below is a version using reduce with single clone (updating initial value passed in to reduce).

const getSubset = (obj, ...keys) => keys.reduce((acc, curr) => {
  acc[curr] = obj[curr]
  return acc
}, {})

const elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
}

const subset = getSubset(elmo, 'annoying', 'height', 'meta')
console.log(subset)
Muhammet Enginar
  • 508
  • 6
  • 14
  • 1
    Awesome! It threw me for a moment not realizing how essential the brackets are on [c]: . I'm assuming that is somehow causing c to be looked at as a value instead of the name of the property. Anyway, very cool. +1 – John Fairbanks Mar 14 '18 at 15:18
  • Thanks mate! That usage is one thing I love about JavaScript which enables generic functions without using eval. Simply, what it makes is letting you set a key of dict to a variable at runtime. if you define var key = 'someKey', then you can use it as { [key]: 'value' }, which gives you { someKey: 'value' }. Really cool. – Muhammet Enginar Mar 15 '18 at 20:49
  • 1
    Should note that you're creating a new object on every iteration though instead of updating a single clone. – mpen May 16 '19 at 06:28
  • 1
    @mpen good find. I've added a version mutating single clone as you've suggested also spreading args instead passing an array of keys. – Muhammet Enginar May 16 '19 at 22:34
8

Dynamic solution

['color', 'height'].reduce((a,b) => (a[b]=elmo[b],a), {})

let subset= (obj,keys)=> keys.reduce((a,b)=> (a[b]=obj[b],a),{});


// TEST

let elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
};

console.log( subset(elmo, ['color', 'height']) );
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
7

The easiest way I found, which doesn't create unnecessary variables, is a function you can call and works identically to lodash is the following:

pick(obj, keys){
    return  Object.assign({}, ...keys.map(key => ({ [key]: obj[key] })))
}

For example:

pick(obj, keys){
    return  Object.assign({}, ...keys.map(key => ({ [key]: obj[key] })))
}
const obj = {a:1, b:2, c:3, d:4}
const keys = ['a', 'c', 'f']
const picked = pick(obj,keys)
console.log(picked)

pick = (obj, keys) => {
  return Object.assign({}, ...keys.map(key => ({
    [key]: obj[key]
  })))
}

const obj = {
  a: 1,
  b: 2,
  c: 3,
  d: 4
}
const keys = ['a', 'c', 'f']

const picked = pick(obj, keys)
console.log(picked)
Costantin
  • 2,486
  • 6
  • 31
  • 48
6

Use pick method of lodash library if you are already using.

var obj = { 'a': 1, 'b': '2', 'c': 3 };

_.pick(object, ['a', 'c']);

// => { 'a': 1, 'c': 3 }

https://lodash.com/docs/4.17.10#pick

Kashif Nazar
  • 20,775
  • 5
  • 29
  • 46
  • 1
    I actually didn't want to install all of the lodash overhead for that use case but I tried all of the reduce methods above and after half an hour I ended up on good old lodash with one clean readable line of code. And as it is the backend it doesn't really matter. – Matthis Kohli Jun 21 '20 at 17:01
  • Same. For react, having an inline pick option is very handy. For example, assigning a subset of props to pass into a component. For example as might be needed to assign initialValues between two componebts wrapped by React Form library. Sure, I could write my own and put it (plus an omit function) into a utilities file. By then, maybe I'm better off importing theirs...lol – SherylHohman Nov 19 '21 at 17:27
6

An Array of Objects

const aListOfObjects = [{
    prop1: 50,
    prop2: "Nothing",
    prop3: "hello",
    prop4: "What's up",
  },
  {
    prop1: 88,
    prop2: "Whatever",
    prop3: "world",
    prop4: "You get it",
  },
]

Making a subset of an object or objects can be achieved by destructuring the object this way.

const sections = aListOfObjects.map(({prop1, prop2}) => ({prop1, prop2}));
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Rahul
  • 121
  • 1
  • 3
5

Using the "with" statement with shorthand object literal syntax

Nobody has demonstrated this method yet, probably because it's terrible and you shouldn't do it, but I feel like it has to be listed.

var o = {a:1,b:2,c:3,d:4,e:4,f:5}
with(o){
  var output =  {a,b,f}
}
console.log(output)

Pro: You don't have to type the property names twice.

Cons: The "with" statement is not recommended for many reasons.

Conclusion: It works great, but don't use it.

ADJenks
  • 2,973
  • 27
  • 38
  • Why not use it? All this hate against `with` is a bit much. Sometimes it's the right tool for the job, possibly including this one, another one being when rendering a template where *all* it's values are coming from `this` or some other object. Don't believe me? Just ask John Resig, author of jQuery. – Dexygen Sep 11 '21 at 21:12
  • 1
    @Dexygen, the page I linked to lists three reasons not to use the `with` statement. Don't get me wrong, I think it's powerful and has its uses. There actually are some problems that I've seen where you can't seem to solve them without using it. Let me rephrase, don't use it *casually*. It's sort of like `eval`, everyone says it's bad and you should never use it, but that's not true, there are places where `eval` *needs* to be used, but it should always be used with careful forethought to make sure you're adding security holes or crippling code optimisation. – ADJenks Sep 14 '21 at 16:43
  • not* adding security holes... – ADJenks Nov 03 '21 at 17:48
4

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 =)

Arthur Ronconi
  • 2,290
  • 25
  • 25
4

If you want to keep more properties than the ones you want to remove, you could use the rest parameter syntax:

const obj = {
  a:1,
  b:2,
  c:3,
  d:4
};
const { a, ...newObj } = obj;
console.log(newObj); // {b: 2, c: 3, d: 4}
3

To add another esoteric way, this works aswell:

var obj = {a: 1, b:2, c:3}
var newobj = {a,c}=obj && {a,c}
// {a: 1, c:3}

but you have to write the prop names twice.

Timar Ivo Batis
  • 1,861
  • 17
  • 21
2

How about:

function sliceObj(obj) {
  var o = {}
    , keys = [].slice.call(arguments, 1);
  for (var i=0; i<keys.length; i++) {
    if (keys[i] in obj) o[keys[i]] = obj[keys[i]];
  }
  return o;
}

var subset = sliceObj(elmo, 'color', 'height');
elclanrs
  • 92,861
  • 21
  • 134
  • 171
2
  1. convert arguments to array

  2. use Array.forEach() to pick the property

    Object.prototype.pick = function(...args) {
       var obj = {};
       args.forEach(k => obj[k] = this[k])
       return obj
    }
    var a = {0:"a",1:"b",2:"c"}
    var b = a.pick('1','2')  //output will be {1: "b", 2: "c"}
    
jhpratt
  • 6,841
  • 16
  • 40
  • 50
olivia
  • 71
  • 1
  • 4
  • 4
    Extending the prototype of native types is considered bad practice, though it would work. _Don't do this if you're writing a library._ – Emile Bergeron May 09 '17 at 18:35
2

This works for me in Chrome console. Any problem with this?

var { color, height } = elmo
var subelmo = { color, height }
console.log(subelmo) // {color: "red", height: "unknown"}
MSi
  • 97
  • 3
2

Like several on this thread I agree with evert that the most obvious old school way of doing this is actually the best available, however for fun let me provide one other inadvisable way of doing it in certain circumstances, say when you already have your subset defined and you want to copy properties to it from another object that contains a superset or intersecting set of its properties.

let set = { a : 1, b : 2, c : 3 };
let subset = { a : null, b : null };
try {
  Object.assign(Object.seal(subset), set);
} catch (e) {
  console.log('its ok I meant to do that <(^.^)^');
}
console.log(subset);
Kyle Zimmer
  • 300
  • 2
  • 7
2

I think this is your answer. (and everyone who is looking for it).

const object = { a: 5, b: 6, c: 7  };
const subset = (({ a, c }) => ({ a, c }))(object);
console.log(subset); // { a: 5, c: 7 }
arlabbafi
  • 86
  • 6
  • how is this different from the first 3 lines of the accepted answer? just copy-paste, but renaming `picked` to `subset`? – user45392 Jan 27 '23 at 17:35
  • 1
    hi dude, honestly when I posted this I didn't find the accepted answer. after you mentioned I saw that. actually, I copied that from medium.com you can search it. – arlabbafi Jan 29 '23 at 15:59
1

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);
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
1

I've got the same problem and solved it easily by using the following libs:

object.pick

https://www.npmjs.com/package/object.pick

pick({a: 'a', b: 'b', c: 'c'}, ['a', 'b'])
//=> {a: 'a', b: 'b'}

object.omit

https://www.npmjs.com/package/object.omit

omit({a: 'a', b: 'b', c: 'c'}, ['a', 'c'])
//=> { b: 'b' }
roNn23
  • 1,532
  • 1
  • 15
  • 32
1

I know it isn't the cleanest, but it's simple and easy to understand.

function obj_multi_select(obj, keys){
    let return_obj = {};
    for (let k = 0; k < keys.length; k++){
        return_obj[keys[k]] = obj[keys[k]];
    };
    return return_obj;
};
  • Much easier to loop through keys instead of indexes: `for (let key of keys) return_obj[key] = obj[key];`. Also snake_case is pretty unconventional for javascript – Gershom Maes Nov 20 '21 at 20:56
0
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");
John
  • 5,942
  • 3
  • 42
  • 79
0

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, ddd: 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, ddd: 8, ab: 5, bc: 6, cd: 7, de: 8  };
const sliceKeys = '({ a, aa, aaa, ab, c, cc, ccc, cd })';

console.log( slice(sliceKeys, object) );
Igor
  • 1,253
  • 1
  • 25
  • 34
0

Extending the builtin prototypes has its advantages. If you use Object.defineProperty you won't produce any pollution - the only remaining concern will be conflict with future properties (e.g. you define Object.prototype.slice, and in the future the ES standard defines Object.prototype.slice as having different functionality - now your code is clobbering the intended functionality that's supposed to exist at Object.prototype.slice).

Also...... ....... . ... .. ..... Elmo's height isn't unknown(!!!)

Object.defineProperty(Object.prototype, 'slice', {
  enumerable: false,
  writable: true,
  value: function(...args) {
    let o = {};
    for (let k of args) this.hasOwnProperty(k) && (o[k] = this[k]);
    return o;
  }
});

elmo = { 
  color: 'red',
  annoying: true,
  height: '24in',
  meta: { one: '1', two: '2'}
};

console.log(elmo.slice('color', 'height'));

console.log('Look, no pollution:');
for (let k in elmo) console.log(`- ${k}`);
Gershom Maes
  • 7,358
  • 2
  • 35
  • 55
-1

Destructuring assignment with dynamic properties

This solution not only applies to your specific example but is more generally applicable:

const subset2 = (x, y) => ({[x]:a, [y]:b}) => ({[x]:a, [y]:b});

const subset3 = (x, y, z) => ({[x]:a, [y]:b, [z]:c}) => ({[x]:a, [y]:b, [z]:c});

// const subset4...etc.


const o = {a:1, b:2, c:3, d:4, e:5};


const pickBD = subset2("b", "d");
const pickACE = subset3("a", "c", "e");


console.log(
  pickBD(o), // {b:2, d:4}
  pickACE(o) // {a:1, c:3, e:5}
);

You can easily define subset4 etc. to take more properties into account.

-1

Try

const elmo={color:"red",annoying:!0,height:"unknown",meta:{one:"1",two:"2"}};

const {color, height} = elmo; newObject = ({color, height});

console.log(newObject); //{ color: 'red', height: 'unknown' }
-1

you can use the comma operator

const elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
};

const subset = ({color , height} = elmo , {color , height});
// {color: 'red', height: 'unknown'}
-1

Worth noting a Zod schema will strip out unknown properties by default. If you're already using Zod, this likely fits right into your development process.

https://github.com/colinhacks/zod

import { z } from "zod";

// muppet schema
const muppet = z.object({
  color: z.string(),
  annoying: z.boolean(),
  height: z.string(),
  meta: z.object({ one: z.string(), two: z.string() }),
});

// TypeScript type if you want it
type TMuppet = z.infer<typeof muppet>;

// elmo example
const elmo: TMuppet = {
  color: "red",
  annoying: true,
  height: "unknown",
  meta: { one: "1", two: "2" },
};

// get a subset of the schema (another schema) if you want
const subset = muppet.pick({ color: true, height: true });

// parsing removes unknown properties by default
subset.parse(elmo); // { color: 'red', height: 'unknown' }
Mark Swardstrom
  • 17,217
  • 6
  • 62
  • 70
-3

Note: though the original question asked was for javascript, it can be done jQuery by below solution

you can extend jquery if you want here is the sample code for one slice:

jQuery.extend({
  sliceMe: function(obj, str) {
      var returnJsonObj = null;
    $.each( obj, function(name, value){
        alert("name: "+name+", value: "+value);
        if(name==str){
            returnJsonObj = JSON.stringify("{"+name+":"+value+"}");
        }

    });
      return returnJsonObj;
  }
});

var elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
};


var temp = $.sliceMe(elmo,"color");
alert(JSON.stringify(temp));

here is the fiddle for same: http://jsfiddle.net/w633z/

ManMohan Vyas
  • 4,004
  • 4
  • 27
  • 40