一线从ES 6中的对象获取某些属性


153

如何编写一种在ES6中仅以最紧凑的方式仅包含很少的属性的函数?

我想出了使用解构+简化对象文字的解决方案,但是我不喜欢代码中重复的字段列表。

有没有更苗条的解决方案?

(v) => {
    let { id, title } = v;
    return { id, title };
}

Answers:


124

尽管不能避免重复字段列表,但这里有些苗条。它使用“参数解构”来避免需要该v参数。

({id, title}) => ({id, title})

(请参阅此其他答案中的可运行示例)。

@EthanBrown的解决方案更为通用。这是它的更惯用的版本,它使用Object.assign和计算的属性([p]部分):

function pick(o, ...props) {
    return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}

如果我们要保留属性的属性(例如和configurable和getter和setter),同时也忽略不可枚举的属性,则:

function pick(o, ...props) {
    var has = p => o.propertyIsEnumerable(p),
        get = p => Object.getOwnPropertyDescriptor(o, p);

    return Object.defineProperties({},
        Object.assign({}, ...props
            .filter(prop => has(prop))
            .map(prop => ({prop: get(props)})))
    );
}

10
+1好答案,torazaburo;谢谢你让我意识到Object.assign; es6就像一棵圣诞树,下面放着很多礼物,假期过后几个月我仍在找礼物
伊桑·布朗

出现错误:属性描述必须是一个对象:未定义。不是filter(...).map(prop => ({[prop]: get(prop)})))吗?
不间断的

对于您的第一个pick()实现,您还可以执行以下操作:return props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts

不幸的是,pick版本在流程或打字稿中不会安全输入。如果要确保类型安全,则无法解构原始对象,然后将每个对象分配给新对象。
duhseekoh

如果对象中不存在属性,则得到undefined。有时很重要。除此之外,好主意。
x-yuri

43

我认为没有任何方法可以使答案比您的答案(或torazburo的答案)更紧凑,但实际上,您要尝试的是模仿Underscore的pick操作。在ES6中重新实现它很容易:

function pick(o, ...fields) {
    return fields.reduce((a, x) => {
        if(o.hasOwnProperty(x)) a[x] = o[x];
        return a;
    }, {});
}

然后,您将获得一个方便的可重用功能:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');

谢谢。这不是我的问题的答案,但是非常好。
kirilloid 2014年

7
(耸耸肩)我觉得这您解决方案的答案;没有更苗条的通用解决方案(torazaburo的解决方案从多余的措辞中删除,但是本质的问题-所有属性名称都必须写两次-意味着它的扩展性不会比您的解决方案更好)。我的解决方案至少可以很好地扩展...对pick函数一次即可,您可以选择所需的任意多个属性,而不会使它们加倍。
伊桑·布朗

1
你为什么用hasOwnProperty?如果是手动选择的字段,则in似乎更合适;尽管我会完全省略支票,只是让他们默认为undefined
Bergi 2015年

Bergi,这是一个合理的观点……我只是认为原型链上的属性(而不是方法)很奇怪,很“臭”(因为它们有代码味),我更喜欢默认将它们过滤掉。如果有一个需要原型属性的应用程序,那么...可以有一个选择。
伊桑·布朗

json数组呢?
Rizwan Patel

19

解决这个问题的诀窍是翻转所采用的方法:与其从原始对象开始,不如从原始对象开始,而是orig从他们想要提取的键开始。

Array#reduce然后,可以使用一个将每个所需的键存储在空对象中,该键作为该initialValue函数的传入对象。

像这样:

const orig = {
  id: 123456789,
  name: 'test',
  description: '…',
  url: 'https://…',
};

const filtered = ['id', 'name'].reduce((result, key) => { result[key] = orig[key]; return result; }, {});

console.log(filtered); // Object {id: 123456789, name: "test"}


11

使用逗号运算符的解决方案略短一些:

const pick = (O, ...K) => K.reduce((o, k) => (o[k]=O[k], o), {})

console.log(
  pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
)  


如何使用这个?你能举个例子吗?
Tomas M,

1
它的工作原理与pick该线程中的其他功能相同:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
shesek

8

TC39的对象剩余/扩展属性提案将使这一过程变得更加巧妙:

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
z; // { a: 3, b: 4 }

(它确实有创建您可能不需要的xy变量的缺点。)


33
这是omitpick
kirilloid的

5
我希望看到一个与ES建议完全相反的变体:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
gfullam

3

您可以使用对象解构从现有对象中解压缩属性,并将其分配给具有不同名称的变量 -新的最初为空的对象的字段。

const person = {
  fname: 'tom',
  lname: 'jerry',
  aage: 100,
}

let newPerson = {};

({fname: newPerson.fname, lname: newPerson.lname} = person);

console.log(newPerson);


(索引):36未捕获到的SyntaxError:无效的销毁分配目标
合并

@Remzes不知道您在哪里以及如何执行此操作,但是它在SO代码编辑器和chrome开发人员工具中效果很好。
萨克斯姆(Saksham)

我使用了jsfiddle
Remzes,

我已经改善了您的答案,但是与OP的要求相比,它仍然太冗长。它不仅重复字段名称,而且重复新对象的名称。
Dan Dascalescu

2

目前有一个草人提议来改进JavaScript的对象简写语法,该语法将允许“挑选”命名属性而无需重复:

const source = {id: "68646", genre: "crime", title: "Scarface"};
const target = {};
Object.assign(target, {source.title, source.id});

console.log(picked);
// {id: "68646", title: "Scarface"}

不幸的是,该提议似乎不会在任何时间很快完成。上次编辑于2017年7月,仍然是草案第0阶段,表明作者可能已放弃或遗忘了它。

ES5及更早版本(非严格模式)

我能想到的最简洁的速记方式涉及到一个没人使用的古老语言功能

Object.assign(target, {...(o => {
    with(o) return { id, title };
})(source)});

with严格模式下禁止使用语句,使这种方法对现代JavaScript的99.999%无效。有点遗憾,因为这是我发现的该with功能的唯一中途使用方法。😀


2

在撰写问题时,ES6是最新的规范。如本答案所述,ES2019中的密钥选择比ES6中的密钥选择显着缩短:

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

1

我有类似于Ethan Brown的解决方案,但pick功能更短。另一个功能pick2更长(更慢),但是允许以类似于ES6的方式重命名属性。

const pick = (o, ...props) => props.reduce((r, p) => p in o ? {...r, [p]: o[p]} : r, {})

const pick2 = (o, ...props) => props.reduce((r, expr) => {
  const [p, np] = expr.split(":").map( e => e.trim() )
  return p in o ? {...r, [np || p]: o[p]} : r
}, {}) 

这是用法示例:

const d = { a: "1", c: "2" }

console.log(pick(d, "a", "b", "c"))        // -> { a: "1", c: "2" }
console.log(pick2(d, "a: x", "b: y", "c")) // -> { x: "1", c: "2" }

1
拒绝投票的原因是什么?它不适合您吗?
亚历山大·普里兹热夫

0

我需要这种解决方案,但我不知道建议的密钥是否可用。因此,我接受了@torazaburo的答案,并针对我的用例进行了改进:

function pick(o, ...props) {
  return Object.assign({}, ...props.map(prop => {
    if (o[prop]) return {[prop]: o[prop]};
  }));
}

// Example:
var person = { name: 'John', age: 29 };
var myObj = pick(person, 'name', 'sex'); // { name: 'John' }

0

受到https://stackoverflow.com/users/865693/shesek的简化方法的启发:

const pick = (orig, ...keys) => keys.reduce((acc, key) => ({...acc, [key]: orig[key]}), {})

用法:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') 结果是: {model: "F40", productionYear: 1987}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.