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

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?

This question is related to javascript

The answer is


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.

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

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.


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

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

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

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

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

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

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:

_x000D_
_x000D_
const getSubset = (obj, ...keys) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});_x000D_
_x000D_
const elmo = { _x000D_
  color: 'red',_x000D_
  annoying: true,_x000D_
  height: 'unknown',_x000D_
  meta: { one: '1', two: '2'}_x000D_
}_x000D_
_x000D_
const subset = getSubset(elmo, 'color', 'annoying')_x000D_
console.log(subset)
_x000D_
_x000D_
_x000D_

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

_x000D_
_x000D_
const getSubset = (obj, ...keys) => keys.reduce((acc, curr) => {_x000D_
  acc[curr] = obj[curr]_x000D_
  return acc_x000D_
}, {})_x000D_
_x000D_
const elmo = { _x000D_
  color: 'red',_x000D_
  annoying: true,_x000D_
  height: 'unknown',_x000D_
  meta: { one: '1', two: '2'}_x000D_
}_x000D_
_x000D_
const subset = getSubset(elmo, 'annoying', 'height', 'meta')_x000D_
console.log(subset)
_x000D_
_x000D_
_x000D_


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


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


ES6 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 the question mentions. Destructuring becomes more complicated if a key is non-alphanumeric, e.g. foo_bar.

The downside is that this requires to duplicate a list of keys, this results in verbose code in case a list is long. Since destructuring duplicates object literal syntax in this case, a list can be copied and pasted as is.

The upside is that it's performant solution that is natural to ES6.

IIFE

let subset = (({ foo, bar }) => ({ foo, bar }))(obj); // dupe ({ foo, bar })

Temporary variables

let { foo, bar } = obj;
let subset = { foo, bar }; // dupe { foo, bar }

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, like pick(obj, 'foo', someKey, ...moreKeys).

A one-liner becomes shorter with each JS edition.

ES5

var subset = Object.keys(obj)
.filter(function (key) { 
  return ['foo', 'bar'].indexOf(key) >= 0;
})
.reduce(function (obj2, key) {
  obj2[key] = obj[key];
  return obj2;
}, {});

ES6

let subset = Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => Object.assign(obj2, { [key]: obj[key] }), {});

Or with comma operator:

let subset = Object.keys(obj)
.filter(key => ['foo', 'bar'].indexOf(key) >= 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});

ES2019

ECMAScript 2017 has Object.entries and Array.prototype.includes, ECMAScript 2019 has Object.fromEntries, they can be polyfilled when needed and make the task easier:

let subset = Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => ['foo', 'bar'].includes(key))
)

A one-liner can be rewritten as helper function similar to Lodash pick or omit where the list of keys is passed through arguments:

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

let subset = pick({ foo: 1, qux: 2 }, 'foo', 'bar'); // { foo: 1 }

A note about missing keys

The major difference between destructuring and conventional Lodash-like pick function is that destructuring includes non-existent picked keys with undefined value in a subset:

(({ foo, bar }) => ({ foo, bar }))({ foo: 1 }) // { foo: 1, bar: undefined }

This behaviour may or not be desirable. It cannot be changed for destructuring syntax.

While pick can be changed to include missing keys by iterating a list of picked keys instead:

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

let subset = inclusivePick({ foo: 1, qux: 2 }, 'foo', 'bar'); // { foo: 1, bar: undefined }

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/


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

Dynamic solution

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

_x000D_
_x000D_
let subset= (obj,keys)=> keys.reduce((a,b)=> (a[b]=obj[b],a),{});_x000D_
_x000D_
_x000D_
// TEST_x000D_
_x000D_
let elmo = { _x000D_
  color: 'red',_x000D_
  annoying: true,_x000D_
  height: 'unknown',_x000D_
  meta: { one: '1', two: '2'}_x000D_
};_x000D_
_x000D_
console.log( subset(elmo, ['color', 'height']) );
_x000D_
_x000D_
_x000D_


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

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

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

Destructuring assignment with dynamic properties

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

_x000D_
_x000D_
const subset2 = (x, y) => ({[x]:a, [y]:b}) => ({[x]:a, [y]:b});_x000D_
_x000D_
const subset3 = (x, y, z) => ({[x]:a, [y]:b, [z]:c}) => ({[x]:a, [y]:b, [z]:c});_x000D_
_x000D_
// const subset4...etc._x000D_
_x000D_
_x000D_
const o = {a:1, b:2, c:3, d:4, e:5};_x000D_
_x000D_
_x000D_
const pickBD = subset2("b", "d");_x000D_
const pickACE = subset3("a", "c", "e");_x000D_
_x000D_
_x000D_
console.log(_x000D_
  pickBD(o), // {b:2, d:4}_x000D_
  pickACE(o) // {a:1, c:3, e:5}_x000D_
);
_x000D_
_x000D_
_x000D_

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


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

It's very easy with destructuring assignment and , operator

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

console.log(picked);
_x000D_
_x000D_
_x000D_


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

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

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

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.


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


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


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!


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