coffeescript
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"
-
It's feature request.
-
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!
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 #4991 (and #1708, #1617).
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.~~
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).
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.
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 }
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 }
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
.
The expression
{a, b} = obj1
evaluates toobj1
.
This is in alignment with JS' destructuring assignment and as such will unlikely change.