648

假设我有一个对象:

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

我想用其属性的子集制作一个新对象。

 // pseudo code
 subset = elmo.slice('color', 'height')

 //=> { color: 'red', height: 'unknown' }

我怎样才能做到这一点?

4

32 回答 32

995

使用对象解构和属性简写

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

console.log(picked); // { a: 5, c: 7 }


来自菲利普·凯维施:

这实际上只是一个被立即调用的匿名函数。所有这些都可以在 MDN 的Destructuring Assignment页面上找到。这是一个扩展的表格

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)

于 2016-09-05T15:12:31.693 回答
216

我建议看看Lodash;它有很多很棒的实用功能。

例如pick(),这正是您所寻求的:

var subset = _.pick(elmo, ['color', 'height']);

小提琴

于 2013-07-22T06:58:48.703 回答
181

如果您使用的是 ES6,则有一种非常简洁的方法可以使用解构来做到这一点。解构允许您使用扩展轻松添加到对象,但它也允许您以相同的方式制作子集对象。

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'};
于 2017-05-23T20:24:12.197 回答
169
于 2019-06-14T06:11:17.197 回答
110

虽然它有点冗长,但您可以使用Array.prototype.reduce完成 2 年前其他人推荐的下划线/lodash 。

var subset = ['color', 'height'].reduce(function(o, k) { o[k] = elmo[k]; return o; }, {});

这种方法从另一方面解决了这个问题:与其获取一个对象并将属性名称传递给它以进行提取,不如获取一个属性名称数组并将它们归约为一个新对象。

虽然在最简单的情况下它更冗长,但这里的回调非常方便,因为您可以轻松满足一些常见要求,例如将新对象的“颜色”属性更改为“颜色”,展平数组等 - 任何从一个服务/库接收对象并在其他地方构建所需的新对象时需要做的事情。虽然 underscore/lodash 是优秀的、良好实现的库,但这是我的首选方法,可减少对供应商的依赖,并且当我的子集构建逻辑变得更复杂时,这是一种更简单、更一致的方法。

编辑:相同的es7版本:

const subset = ['color', 'height'].reduce((a, e) => (a[e] = elmo[e], a), {});

编辑:也是一个很好的柯里化例子!让“pick”函数返回另一个函数。

const pick = (...props) => o => props.reduce((a, e) => ({ ...a, [e]: o[e] }), {});

上面的方法与另一种方法非常接近,除了它可以让您动态构建一个“选择器”。例如

pick('color', 'height')(elmo);

这种方法特别巧妙的是,您可以轻松地将选择的“选择”传递给任何需要函数的东西,例如Array#map

[elmo, grover, bigBird].map(pick('color', 'height'));
// [
//   { color: 'red', height: 'short' },
//   { color: 'blue', height: 'medium' },
//   { color: 'yellow', height: 'tall' },
// ]
于 2015-08-24T13:54:04.877 回答
54

我添加这个答案是因为没有使用任何答案Comma operator

destructuring assignment使用和,操作员非常容易

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

console.log(picked);

于 2019-02-10T03:17:59.143 回答
49

核心库没有内置类似的东西,但您可以使用对象解构来做到这一点......

const {color, height} = sourceObject;
const newObject = {color, height};

你也可以写一个实用函数来做......

const cloneAndPluck = function(sourceObject, keys) {
    const newObject = {};
    keys.forEach((obj, key) => { newObject[key] = sourceObject[key]; });
    return newObject;
};

const subset = cloneAndPluck(elmo, ["color", "height"]);

Lodash 等库也有_.pick().

于 2013-07-22T06:51:25.753 回答
42

另一种解决方案:

var subset = {
   color: elmo.color,
   height: elmo.height 
}

到目前为止,这对我来说比几乎任何答案都更具可读性,但也许这只是我!

于 2018-04-11T18:43:53.337 回答
27

打字稿解决方案:

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

输入信息甚至允许自动完成:

归功于DefinitiveTypedU extends keyof T诡计!

打字稿游乐场

于 2019-05-16T06:30:20.460 回答
22

我想在这里提一下非常好的策展:

选择-es2019.js

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

选择-es2017.js

Object.entries(obj)
.filter(([key]) => ['whitelisted', 'keys'].includes(key))
.reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});

选择-es2015.js

Object.keys(obj)
.filter((key) => ['whitelisted', 'keys'].indexOf(key) >= 0)
.reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})

省略-es2019.js

Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => !['blacklisted', 'keys'].includes(key))
);

省略-es2017.js

Object.entries(obj)
.filter(([key]) => !['blacklisted', 'keys'].includes(key))
.reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});

省略-es2015.js

Object.keys(obj)
.filter((key) => ['blacklisted', 'keys'].indexOf(key) < 0)
.reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})
于 2020-07-20T10:45:38.880 回答
13

你也可以使用Lodash

var subset = _.pick(elmo ,'color', 'height');

作为补充,假设您有一个“elmo”数组:

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

如果您想要相同的行为,使用 lodash,您只需:

var subsets = _.map(elmos, function(elm) { return _.pick(elm, 'color', 'height'); });
于 2014-08-06T12:12:02.067 回答
10

正如这个问题所讨论的,在 JavaScript 中解构为动态命名的变量是不可能的。

动态设置键,您可以使用 reduce 函数而不改变对象,如下所示:

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)

应该注意,您在每次迭代时都创建一个新对象,而不是更新单个克隆。– mpen

下面是使用带有单个克隆的 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)

于 2017-12-21T01:50:38.543 回答
8

动态解决方案

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

于 2019-03-05T05:51:30.293 回答
7

pick如果您已经在使用,请使用lodash 库的方法。

var obj = { 'a': 1, 'b': '2', 'c': 3 };

_.pick(object, ['a', 'c']);

// => { 'a': 1, 'c': 3 }

https://lodash.com/docs/4.17.10#pick

于 2018-07-27T06:13:42.363 回答
5

对象数组

const aListOfObjects = [{
    prop1: 50,
    prop2: "Nothing",
    prop3: "hello",
    prop4: "What's up",
  },
  {
    prop1: 88,
    prop2: "Whatever",
    prop3: "world",
    prop4: "You get it",
  },
]

可以通过以这种方式解构对象来制作一个或多个对象的子集。

const sections = aListOfObjects.map(({prop1, prop2}) => ({prop1, prop2}));
于 2020-12-09T01:42:08.260 回答
5

我发现的最简单的方法(不会创建不必要的变量)是您可以调用的函数,并且与 lodash 的工作方式相同,如下所示:

pick(obj, keys){
    return  Object.assign({}, ...keys.map(key => ({ [key]: obj[key] })))
}

例如:

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)

于 2021-04-18T19:10:57.877 回答
4

只是另一种方式...

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]

您可以将此函数与对象数组一起使用 =)

于 2017-09-19T23:30:42.893 回答
4

使用带有速记对象字面量语法的“with”语句

还没有人演示过这种方法,可能是因为它很糟糕,你不应该这样做,但我觉得它必须列出。

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

优点:您不必输入两次属性名称。

缺点:不推荐使用“ with ”语句,原因有很多。

结论:它很好用,但不要使用它。

于 2020-10-15T21:38:57.260 回答
3

要添加另一种深奥的方式,这也可以:

var obj = {a: 1, b:2, c:3}
var newobj = {a,c}=obj && {a,c}
// {a: 1, c:3}

但你必须写两次道具名称。

于 2020-10-29T00:15:55.640 回答
2

怎么样:

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');
于 2013-07-22T06:53:21.690 回答
2
  1. 将参数转换为数组

  2. 用来Array.forEach()挑选属性

    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"}
    
于 2016-09-22T16:17:42.223 回答
2

这适用于我在 Chrome 控制台中。这有什么问题吗?

var { color, height } = elmo
var subelmo = { color, height }
console.log(subelmo) // {color: "red", height: "unknown"}
于 2017-07-19T16:40:55.833 回答
2

就像这个线程上的几个一样,我同意 evert 最明显的老派方法实际上是最好的,但是为了好玩,让我在某些情况下提供另一种不建议的方法,比如当你已经定义了你的子集时并且您希望将属性从另一个对象复制到它,该对象包含其属性的超集或相交集。

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

于 2021-06-22T17:00:00.663 回答
1

我遇到了同样的问题,并通过使用以下库轻松解决了它:

对象.pick

https://www.npmjs.com/package/object.pick

pick({a: 'a', b: 'b', c: 'c'}, ['a', 'b'])
//=> {a: 'a', b: 'b'}

对象.省略

https://www.npmjs.com/package/object.omit

omit({a: 'a', b: 'b', c: 'c'}, ['a', 'c'])
//=> { b: 'b' }
于 2020-06-30T08:29:03.223 回答
1

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

这个答案也使用了神奇的逗号运算符: https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

如果你想变得非常花哨,这更紧凑:

const r = Object.keys(selectable).reduce((a, b) => (a[b] = v[b], a), {});

将它们组合成一个可重用的函数:

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);
于 2018-05-26T02:18:31.773 回答
0
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");
于 2013-07-22T06:59:59.117 回答
0

将我的 2 美分加到Ivan Nosov的答案中:

就我而言,我需要从对象中“切出”许多键,因此它变得非常难看,而且不是一个非常动态的解决方案:

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

所以这是一个使用 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) );
于 2019-12-16T13:21:57.313 回答
0

具有动态属性的解构赋值

此解决方案不仅适用于您的具体示例,而且更普遍适用:

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

您可以轻松定义subset4等以考虑更多属性。

于 2017-02-18T21:10:05.677 回答
0

扩展内置原型有其优势。如果您使用Object.defineProperty,您将不会产生任何污染 - 唯一剩下的问题是与未来的属性冲突(例如,您定义Object.prototype.slice,并且在未来 ES 标准定义Object.prototype.slice为具有不同的功能 - 现在您的代码正在破坏应该的预期功能存在于Object.prototype.slice)。

还...... ....... 。... .. ..... Elmo 的身高不为人知(!!!)

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

于 2021-11-18T08:20:52.370 回答
0

我知道这不是最干净的,但它简单易懂。

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;
};
于 2021-11-20T06:02:13.763 回答
-1

尝试

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' }
于 2019-04-12T04:53:50.763 回答
-3

注意:虽然最初提出的问题是针对 javascript,但可以通过以下解决方案使用 jQuery

如果你愿意,你可以扩展 jquery 这里是一个切片的示例代码:

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

这是相同的小提琴:http: //jsfiddle.net/w633z/

于 2013-07-22T07:07:34.023 回答