coffeescript icon indicating copy to clipboard operation
coffeescript copied to clipboard

Proposal: pre-compile "obj{ a, b }" to "(({a,b})->{a,b})(obj)" to create a copy of "obj" with only properties "a" and "b"

Open eugene-piatenko opened this issue 4 years ago • 9 comments

  1. It's feature request.

  2. Description:

    it's a very common pattern when you need to pickup only some properties from object and to do this people usually use such constructs:

Input Code

obj1 = a:1, b:2, c:3
obj2 = ( ({a,b}) -> {a,b} ) obj1

Possible Solution

would be nice to have an "operator" like this:

obj1 = a:1, b:2, c:3
obj2 = obj1{ a, b }

as we can do

obj1 = a:1, b:2, c:3
{ a, b } = obj1
obj2 = { a, b }

but it destruct local variables "a" and "b" or

obj1 = a:1, b:2, c:3
obj2 = ( ({a,b}) -> {a,b} ) obj1

that way is safe but looks ugly )

Thank you!

eugene-piatenko avatar Jun 26 '20 05:06 eugene-piatenko

It sounds like you're basically asking for the inverse of object spread, which creates a larger object (i.e. obj3 = { obj1..., d:4 }). I'm not sure there's a succinct way to do this currently. I've most often seen it done like this:

obj1 = a:1, b:2, c:3
obj2 =
  a: obj1.a
  b: obj1.b

It's a bit repetitive, but it avoids extra local variables. Or to get closer to your example, you can create a scope around the destructuring to isolate those variables:

obj1 = a:1, b:2, c:3
obj2 = do -> { a, b } = obj1; { a, b }
# obj2 is now {a:1, b:2}

See example.

GeoffreyBooth avatar Jun 26 '20 23:06 GeoffreyBooth

See #4991 (and #1708, #1617).

vendethiel avatar Jun 27 '20 11:06 vendethiel

I've run into this a bunch lately, and find it ineligant that all existing solutions need to repeat a and b at least twice.

A possible extension to destructuring I thought of that I was a bit surprised didn't work is the following:

obj1 = {a: 1, b: 2, c: 3}
obj2 = {}
{obj2.a, obj2.b} = obj1

Intended complication:

var obj1, obj2;
obj1 = {a: 1, b: 2, c: 3};
obj2 = {};
obj2.a = obj1.a
obj2.b = obj1.b

Do you think this would be interesting, or is it confusing notationally? It requires repeating obj2 a bunch, but at least not a and b.

~~Actually, now I realize @GeoffreyBooth's last example can be simplified to:~~

obj1 = {a: 1, b: 2, c: 3}
obj2 = do -> {a, b} = obj1

~~which it pretty nice: nothing repeated.~~

edemaine avatar Jun 27 '20 12:06 edemaine

Unfortunately

obj1 = {a: 1, b: 2, c: 3}
obj2 = do -> {a, b} = obj1

does not work (( it will return obj1 as this example also:

obj2 = {a, b} = obj1

But now I see a lot of people discussing it >> #4991 (and #1708, #1617).

eugene-piatenko avatar Jun 27 '20 19:06 eugene-piatenko

How about:

pick = (obj, keys...) ->
  if keys.length is 1 and Array.isArray keys[0]
    keys = keys[0]

  Object.assign {}, ([key]: obj[key] for key in keys)...

obj1 = a: 1, b: 2, c: 3
obj2 = pick obj1, 'a', 'b'

If you don't like the many quotes in 'a', 'b'...

qw = (s) -> s.split /\s+/ # like Perl's qw{} operator
obj2 = pick obj1, qw 'a b'

I'm not convinced the requested feature is justified.

rdeforest avatar Jul 17 '20 22:07 rdeforest

How about:

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

var object = { 'a': 1, 'b': '2', 'c': 3 };
 
_.pick(object, ['a', 'c']);
// => { 'a': 1, 'c': 3 }

GeoffreyBooth avatar Jul 18 '20 22:07 GeoffreyBooth

Here's another one: #try

pick = (cb) -> Reflect.apply cb, res = {}, []; res

obj1 = a:1, b:2, c:3
obj2 = pick ->
  { @a, @b } = obj1

I generally found Reflect patterns to be the most viable option for accessing several props since it lets you use the @prop shorthand. Though in this case I'd just copy-paste the property list/object to do something like @GeoffreyBooth suggested above:

obj2 = do ->
  { a, b } = obj1
  { a, b }

An unfortunate sideeffect of this pattern is that you have to pay attention to variable scope and are conditionally required to shadow your props:

a = "something else"
obj2 = do (a, b) ->  # ReferenceError: b is not defined
  { a, b } = obj1
  { a, b }

Inve1951 avatar Jul 19 '20 15:07 Inve1951

The expression {a, b} = obj1 evaluates to obj1. That feels kind of wrong, since that line is doing something to obj1 and I would expect the expression to evaluate to the result and not the input of the operation.

If it did evaluate to the left side we could just write obj2 = {a, b} = obj1.

JanMP avatar Oct 18 '20 21:10 JanMP

The expression {a, b} = obj1 evaluates to obj1.

This is in alignment with JS' destructuring assignment and as such will unlikely change.

Inve1951 avatar Oct 23 '20 11:10 Inve1951