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

2018-12-31 17:01发布

问题:

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?

回答1:

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)



回答2:

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



回答3:

If you are using ES6 there is a very concise way to do this using destructing. Destructing 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 \"d\" field 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\'};


回答4:

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


回答5:

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



回答6:

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


回答7:

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!



回答8:

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 = (keys, obj) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});

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

console.log(getSubset([\'color\', \'annoying\'], elmo));



回答9:

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



回答10:

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


回答11:

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



回答12:

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


回答13:

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.



回答14:

  1. convert arguments to array

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

    Object.prototype.pick = function() {
       var obj = {};
       var args = arguments
       Array.from(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\"}
    


回答15:

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


回答16:

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


回答17:

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/



标签: javascript