Destructuring an object into a new object (like Underscore pick but ESier)

# Zacqary Adam Xeper (9 years ago)

So let's say I have: let obj = {foo: 1, bar: 2, baz: 3}

And I want to initialize a new object that looks like: {foo: obj.foo, bar: obj.bar, quux: 4}

I can create a new object with ALL of these properties using a spread operator: let newObj = {...obj, quux: 4}

But if I want to initialize a new object that pulls in just the foo and bar properties, I have to type this whole structure out: let newObj = {foo: obj.foo, bar: obj.bar, quux: 4}

Or, if I want to use shorthand property names, I can use a destructuring statement, but then I have to type all the property names twice: let {foo, bar} = obj; let newObj = {foo, bar, quux: 4};

Underscore.js has a pick function, which looks like this: let newObj = {..._(obj).pick(["foo", "bar"]), quux: 4};

but that requires Underscore as a dependency and the syntax is still pretty annoying. I feel like this is a common enough task — especially when trying to avoid mutating data — that there ought to be a native ES way of doing this.

For example, taking a cue from the computed property syntax: let newObj = { {foo, bar}: obj, quux: 4 }

This also echoes ordinary destructuring syntax, where the variable names are on the left of the equals sign and the object is on the right. Here, it's the same, except for initializing objects.

# Bradley Meck (9 years ago)
# Bob Myers (9 years ago)

Indeed, this is the exact objective of the pick notation which I proposed about six months ago, and have now greatly refined and also implemented a proof-of-concept.

In the previous incarnation, I called this a "pick operator" and represented it with #, but now I treat it as an extension of dot notation.

What you want is represented as something like

let newObj = obj.{foo, bar};

See rtm/js-pick-notation/blob/master/docs/intro.md for a friendly intro. That repo also contains a spec and the POC with tests.

, Bob Myers

# Kevin Smith (9 years ago)

I can create a new object with ALL of these properties using a spread operator: let newObj = {...obj, quux: 4}

Let's not do this.

# /#!/JoePea (9 years ago)

Bob, the benefit of your proposed syntax would be that the object identifier comes first instead of last, so the reader can see the intent of the code sooner:

let newObj = {...obj.{foo, bar}, quux:4}

/#!/JoePea

# Zacqary Adam Xeper (9 years ago)

I like how your pick notation can combine easily with spread operators:

let newObj = { ...obj.{foo, bar}, quux: 4}

I think I actually prefer that to mine. Destructuring on the left side of a colon is sort of fighting against the spread operator, and might suggest a more natural syntax of { {*}: obj } instead of {...obj}. And I don't like that.

# Bergi (9 years ago)

Zacqary Adam Xeper wrote:

But if I want to initialize a new object that pulls in just the foo and bar properties, I have to type this whole structure out: let newObj = {foo: obj.foo, bar: obj.bar, quux: 4}

Or, if I want to use shorthand property names, I can use a destructuring statement, but then I have to type all the property names twice: let {foo, bar} = obj; let newObj = {foo, bar, quux: 4};

I would suggest that we also allow property references, not only variable identifiers in shorthand property notation:

let newObj = {obj.foo, obj.bar, quux: 4};

What do you think?

, Bergi

# Bergi (9 years ago)

Viktor Kronvall schrieb:

What would this do?

let newObj = {obj.a.b}

{a: {b: obj.a.b}} or {a: obj.a.b}?

Or should nested property access be disallowed?

Neither - obj.a.b is (obj.a).b, so it's equivalent to let newObj = {b: obj.a.b}. The spec would basically be something like

| Let propKey be GetReferencedName(exprValue)

and otherwise the same.

# Bob Myers (9 years ago)

In my proposal, there are two syntaxes related to "deep" or "nested" picking.

Assuming:

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

Inside the picking construct, the dot does a "deep pick":

obj.{a.b} // { b: 1 }

The colon does a "nested pick":

obj.{a: {b}} // { a: {b: 1}}

Bob