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

后端 未结 27 1683
刺人心
刺人心 2020-11-21 22:46

Say I have an object:

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

I want to m

相关标签:
27条回答
  • 2020-11-21 23:21

    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 讨论(0)
  • 2020-11-21 23:24

    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' }
    
    0 讨论(0)
  • 2020-11-21 23:24

    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, ffffd: 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, ffffd: 8, ab: 5, bc: 6, cd: 7, de: 8  };
    const sliceKeys = '({ a, aa, aaa, ab, c, cc, ccc, cd })';
    
    console.log( slice(sliceKeys, object) );
    
    0 讨论(0)
  • 2020-11-21 23:26

    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 }
    
    0 讨论(0)
  • 2020-11-21 23:26

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

    0 讨论(0)
  • 2020-11-21 23:26
    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");
    
    0 讨论(0)
提交回复
热议问题