798

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?

2

29 Answers 29

1191

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)

17
  • 76
    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 }); Commented Jan 27, 2017 at 11:41
  • 18
    is there a way to do it dynamically with the spread operator? Commented Jun 13, 2017 at 17:24
  • 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. Commented Aug 28, 2017 at 2:13
  • 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. Commented Jul 6, 2018 at 6:24
  • 67
    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. Commented Oct 9, 2018 at 19:29
351

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>
)
8
  • 6
    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
    Commented Sep 29, 2020 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. Commented Jun 11, 2021 at 18:05
  • 4
    @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
    Commented Jun 11, 2021 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. Commented Jun 11, 2021 at 20:11
  • I would prefer .filter(key => obj.hasOwnProperty(key)) over .filter(key => key in obj). Commented Nov 20, 2021 at 21:09
262

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

9
  • 2
    same for underscore.js
    – Dan
    Commented Nov 28, 2016 at 4: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. Commented Jul 3, 2018 at 1:01
  • 24
    yep! you can use _.omit(elmo, ['voice']) to return everything but voice
    – xavdid
    Commented Jul 4, 2018 at 1: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
    Commented May 12, 2020 at 18:03
  • 2
    You don't need underscore/lodash to accomplish that. I believe vanilla js solutions are better.
    – iedmrc
    Commented Jul 28, 2020 at 8:57
203

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'};
2
  • 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 Commented May 26, 2018 at 2: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
    Commented May 27, 2018 at 19:16
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' },
// ]
6
  • 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.) Commented Mar 7, 2016 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. Commented Apr 8, 2016 at 16:43
  • 10
    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
    Commented Feb 24, 2017 at 18:52
  • 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] } }), {}); Commented Jul 11, 2017 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. Commented Jul 11, 2017 at 11:30
63

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

It's very easy with destructuring assignment and the , operator:

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

console.log(picked);

4
  • 4
    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
    Commented Jul 18, 2019 at 6:55
  • 22
    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.) Commented Aug 15, 2019 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. Commented Feb 13, 2020 at 11:16
  • 4
    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}. Commented May 12, 2020 at 8:17
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!

7
  • 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
    Commented Nov 8, 2018 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
    Commented Jan 10, 2020 at 7:57
  • I wouldn't call the destructuring shorthand less error prone as it's not D.R.Y.
    – gman
    Commented Apr 7, 2020 at 4: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
    Commented Apr 18, 2020 at 2: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
    Commented Apr 7, 2021 at 5:05
57

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(key => { newObject[key] = sourceObject[key]; });
    return newObject;
};

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

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

0
37

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

5
  • @mpen how would you modify the example to be able to pass const allowedKeys:string[] = ["a","b"]? This would help me answer stackoverflow.com/questions/75047175/…
    – Victor
    Commented Jan 8, 2023 at 12:29
  • @Victor You should change the type of allowedKeys to Array<keyof YourType>. 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
    Commented Jan 9, 2023 at 1: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<T, U> is safer Commented Apr 2, 2023 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
    Commented Apr 3, 2023 at 2: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 Commented Apr 3, 2023 at 2: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] }), {})
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'); });
9

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)

1
  • 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. Commented Mar 15, 2018 at 20:49
7

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']) );

4
  • does not work for nested items
    – Alexey Sh.
    Commented May 17 at 16:43
  • @AlexeySh. OP is asking about the properties of the object - so what nested items are you talking about? Commented May 18 at 19:32
  • Obviously, an object can contain an object property. So nested properties are the special case. This is how JS and TS work.
    – Alexey Sh.
    Commented Jul 10 at 22:18
  • @AlexeySh. I do not agree with you. OP asks about object properties, not about "nested object properties". Although someone could add an answer that takes this into account as well. Commented Jul 10 at 22:41
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)

0
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.

4
  • 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
    Commented Sep 11, 2021 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
    Commented Sep 14, 2021 at 16:43
  • not* adding security holes...
    – ADJenks
    Commented Nov 3, 2021 at 17:48
  • 1
    with is deprecated. But otherwise I like your solution the most
    – igo
    Commented Nov 25, 2023 at 11:54
5

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}
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"}
1
  • 6
    This reads nice, but creates two unnecessary variables, color and height. Commented Nov 28, 2018 at 18:03
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);

0
2

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.

3
  • This does not work, actually.
    – Ry-
    Commented Sep 26, 2023 at 21:40
  • @Ry- added brackets works now at least in Chrome Console. Commented Jul 2 at 2:27
  • a and c are still undeclared, which will fail in strict mode (which should always be used and is automatically on inside classes and modules).
    – Ry-
    Commented Jul 2 at 2:35
1

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');
2
  • This would fail if the value of the property was false (or falsy). jsfiddle.net/nfAs8
    – Alxandr
    Commented Jul 22, 2013 at 6:57
  • 1
    That's why I changed it to keys[i] in obj.
    – elclanrs
    Commented Jul 22, 2013 at 6:58
1
  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"}
    
1
  • 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. Commented May 9, 2017 at 18:35
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' }
1

If you're looking to extract specific properties from an object into variables and simultaneously create a subset of that object you can use the following:

const theObject = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const subset = { ...({ a, c, e} = new Proxy({}, { get: (t, p) => (t[p] = theObject[p], t[p]) })) }
console.log(a)      // 1
console.log(subset) // {a: 1, c: 3, e: 5}

This works because the get() trap of the proxy will record only the properties accessed in the destructuring { a, c, e} = proxy so { ...proxy } will contain only that subset of theObject

fiddle

1

Many answers I saw don't work with array of strings or they are way too much verbose.

I propose this solution that is a potential alternative to the lodash pick method but it's just one line long.

First thing define a pick list:

const pickList = ['color', 'height']

Then use:

pickList.reduce((acc, cur) => ({...acc, [cur]: elmo[cur]}), {})
0

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);
0

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' }
-1

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) );
1
  • Yikes. Use a loop.
    – Ry-
    Commented Sep 26, 2023 at 21:45
-2

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.

-4

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/

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.