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

后端 未结 27 1684
刺人心
刺人心 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条回答
  • 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' }
    
    0 讨论(0)
  • 2020-11-21 23:27

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

    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)

    0 讨论(0)
  • 2020-11-21 23:29

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

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

    0 讨论(0)
  • 2020-11-21 23:31

    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

    0 讨论(0)
提交回复
热议问题