How to get a subset of a javascript object's properties

JavascriptObject

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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)

Solution 2 - Javascript

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

Solution 3 - Javascript

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

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

Solution 4 - Javascript

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

Solution 5 - Javascript

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' },
// ]

Solution 6 - Javascript

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

Solution 7 - Javascript

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().

Solution 8 - Javascript

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!

Solution 9 - Javascript

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

Solution 10 - Javascript

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] }), {})

Solution 11 - Javascript

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

Solution 12 - Javascript

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)

Solution 13 - Javascript

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

Solution 14 - Javascript

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

Solution 15 - Javascript

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)

Solution 16 - Javascript

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

Solution 17 - Javascript

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

Solution 18 - Javascript

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.

Solution 19 - Javascript

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.

Solution 20 - Javascript

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

Solution 21 - Javascript

  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"}
    

Solution 22 - Javascript

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"}

Solution 23 - Javascript

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

Solution 24 - Javascript

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

Solution 25 - Javascript

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

Solution 26 - Javascript

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}

Solution 27 - Javascript

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

Solution 28 - Javascript

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.

Solution 29 - Javascript

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

Solution 30 - Javascript

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

Solution 31 - Javascript

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;
};

Solution 32 - Javascript

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

Solution 33 - Javascript

> 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/

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChristian SchlenskerView Question on Stackoverflow
Solution 1 - JavascriptIvan NosovView Answer on Stackoverflow
Solution 2 - JavascriptYggView Answer on Stackoverflow
Solution 3 - JavascriptEstus FlaskView Answer on Stackoverflow
Solution 4 - JavascriptLaurenView Answer on Stackoverflow
Solution 5 - JavascriptJosh from QaribouView Answer on Stackoverflow
Solution 6 - JavascriptCode ManiacView Answer on Stackoverflow
Solution 7 - JavascriptalexView Answer on Stackoverflow
Solution 8 - JavascriptEvertView Answer on Stackoverflow
Solution 9 - JavascriptmpenView Answer on Stackoverflow
Solution 10 - JavascriptiedmrcView Answer on Stackoverflow
Solution 11 - JavascriptArthur AlvimView Answer on Stackoverflow
Solution 12 - JavascriptMuhammet EnginarView Answer on Stackoverflow
Solution 13 - JavascriptKamil KiełczewskiView Answer on Stackoverflow
Solution 14 - JavascriptKashif NazarView Answer on Stackoverflow
Solution 15 - JavascriptCostantinView Answer on Stackoverflow
Solution 16 - JavascriptRahul View Answer on Stackoverflow
Solution 17 - JavascriptArthur RonconiView Answer on Stackoverflow
Solution 18 - JavascriptADJenksView Answer on Stackoverflow
Solution 19 - JavascriptTimar Ivo BatisView Answer on Stackoverflow
Solution 20 - JavascriptelclanrsView Answer on Stackoverflow
Solution 21 - JavascriptoliviaView Answer on Stackoverflow
Solution 22 - JavascriptMSiView Answer on Stackoverflow
Solution 23 - JavascriptKyle ZimmerView Answer on Stackoverflow
Solution 24 - JavascriptAlexander MillsView Answer on Stackoverflow
Solution 25 - JavascriptroNn23View Answer on Stackoverflow
Solution 26 - JavascriptChristopher SuárezView Answer on Stackoverflow
Solution 27 - JavascriptJohnView Answer on Stackoverflow
Solution 28 - Javascriptuser6445533View Answer on Stackoverflow
Solution 29 - JavascriptIgorView Answer on Stackoverflow
Solution 30 - JavascriptGershom MaesView Answer on Stackoverflow
Solution 31 - Javascriptjust_another_beginnerView Answer on Stackoverflow
Solution 32 - Javascriptmohan kumarView Answer on Stackoverflow
Solution 33 - JavascriptManMohan VyasView Answer on Stackoverflow