const b = a.{ id, name, age } - Object Cherry Pick

I am not aware of this proposal, but it should probably be more like this

when({ headers: otherHeaders.{foo, bar}, ...rest }) { // Using variables
when({ headers: {'fooValue', 'barValue'}, ...rest }) { // Using values

You need an assignment.

But from your point of view I understand you want, by default if no assignment is present, to self-assign the new Object to a variable name identical to its source object.

Like if a.{ b, c } was auto assigned to a.
It would transform a.{ b, c } into a = a.{ b, c }
Which is quite dangerous because it would destroy the source object.
While it can be convenient in some cases, there will be different cases where you want to preserve the source object.
Which is why self-assignment cannot be the default behavior.
You need to specify the variable in which you want the result to come in.

I mean just consider the patterns:

// valid js
const arrFn = ([ foo, bar, baz ]) => console.log(foo, bar, baz)
const objFn = ({ foo, bar, baz }) => console.log(foo, bar, baz)
// we can access foo, bar & baz within the arrow function
// but we can't access the passed in object/array itself

arrFn([1, 2, 3])
objFn({ a: 1, b: 2, c: 3 })

This works fine but what if we need to get the length of the array that was passed in or the prototype of the passed in object; We can't do that using the current syntax;
but with explicit reference like

const arrFn = (arrRef.[ foo, bar, baz ]) => console.log(arrRef.length, foo, bar, baz)
const objFn = (objRef.{ foo, bar, baz }) => console.log(Object.getPrototypeOf(objRef), foo, bar, baz)

arrFn([1, 2, 3])
objFn({ a: 1, b: 2, c: 3 })

The argument we pass into arrFn function on calling it is assigned/bounded to the parameter named arrRef. Then we are picking first three items from it, bounded to the identifiers named foo, bar & baz. It is what's happening with this pattern when using it to define a function.

The way we do this with the current syntax would be:

const arrRef = [1, 2, 3]
const objRef = { a: 1, b: 2, c: 3 }

const arrFn = ([ foo, bar, baz ]) => console.log(arrRef.length, foo, bar, baz)
const objFn = ({ foo, bar, baz }) => console.log(Object.getPrototypeOf(objRef), foo, bar, baz)

arrFn(arrRef)
objFn(objRef)

I think you are confused with what is a pattern and what is an executed expression. I had that too when learning Haskell!
Here's a link to the pattern matching proposal so you can understand them better...

If you need the whole object, then use simple named parameter and destructure into local variables.
Or use a default second argument, if you're not worried someone will pass a different object in there:

const arrFn = (arrRef, [ foo, bar, baz ] = arrRef) => console.log(arrRef.length, foo, bar, baz)
const objFn = (objRef, { foo, bar, baz } = objRef) => console.log(Object.getPrototypeOf(objRef), foo, bar, baz)
1 Like

Or pass the reference in parameters, then destructure inside function body.

const arrFn = (arrRef) => { const [ foo, bar, baz ] = arrRef; }
const objFn = (objRef) => { const { foo, bar, baz } = objRef; }

This need is doable today and doesn't seem to intersect with the proposal.

Indeed. Destructuring syntax generally tends to look the same as "construction" syntax, but performs the opposite behavior. i.e. { x: X, y: Y } will construct an object, binding properties x and y to X and Y, while that same syntax in an assignment position would instead get the x and y properties and bind them to X and Y. We're either constructing or destructing an object.

The "obj.{ x, y }" is not a "construction" syntax, so there isn't a reasonable way to make an oppositte version in an assignment location for destructuring purposes. What you're proposing is not an opposite to "obj.{ x, y }", rather, it's a completly different meaning for the same syntax, when used in a different location.

Now you understand!!
This is just a plus that we can use if the proposed syntax becomes available.
If the syntax is valid as an expression, why not leverage it in pattern matching??

exactly! that's a major thing this tries to simplify; enabling parameter properties.
as @lightmare said this would be similar to:

But different meanings in different locations lacks consistency, nope ?

I supposed we could attempt to find an opposite to the "obj.{ x, y }" syntax (for demonstration purposes, I don't want to actually do this). What it normally means is "From the object 'obj', take properties x and y, and place them on a new object". So, the reverse would have to be something like "From the target object, take properties x and y and place them on the source object 'obj'", or something like that. There's different ways to argue over what the reverse might look like, but I don't see any scenario where the reverse would also make "x" and "y" into local variables. For that to happen, the "obj.{ x, y }" syntax must use local variables "x" and "y", which it does not do.

that's a thing in destructuring form and literal;
see:

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

the destructuring involves renaming keys which looks like key: value pair in literal form. Does that seem consistant? well it's consistant in a way how this pattern works with assignments. I hope a.{//...} in function parameters does look as gnarly.

remember the behaviour of ... operator?
It functions in opposite ways depending on the situation; i.e, rest/spread.

(...people) => console.log("hi ", people)

takes every argument into people

console.log(...people)

takes everything out of people
Does that break consistency; it seems not!
Why can't we use id.{...args} in two different contexts like this??

Spread seems perfectly consistent to me - ...people spreads a single variable into multiple locations, or, collects values from multiple locations together and binds it into a single variable. It's performing the opposite action, depending on where its located.

For example:

// Example 1

const x = 1
const y = 2
const others = [3, 4, 5]
const result = [x, y, ...others]
result // [1, 2, 3, 4, 5]

// Now let's do that in reverse
// We'll start by assigning the final values
// then destructure
// then see that we end up with the original values

result = [1, 2, 3, 4, 5]
const [x, y, ...others] = result
x // 1
y // 2
others // [3, 4, 5]


// Example 2

const x = 1
const y = 2
const others = { z: 3 }
const result = { myX: x, y, ...others }
result // { myX: 1, y: 2, z: 3 }

// Now let's do that in reverse!

const result = { myX: 1, y: 2, z: 3 }
const { myX: x, y, ...others } = result
x // 1
y // 2
others // { z: 3 }


// Example 3

const obj = { a: 1, b: 2 }
const result = obj.{ a }
result // { a: 1 }

// Now let's do that in reverse!

const result = { a: 1 }
const obj.{ a } = result
obj // Should be { a: 1, b: 2 } if this were a proper reversal (of course, this is impossible to do - we're not starting with enough information to be able to construct this final result)
a // Shouldn't be defined if this were a proper reversal
1 Like

That is plain wrong man! what does that even mean??
N.B: please note the word structure, as I'm not referring to the literal use of id.{...args} to avoid confusions.
I never suggested this structure be used outside the ArgumentList of a function definition. What does a spread mean all on its own / in any isolated context? A similar thing could be said about this.
Here's an example use case:

function login(person.{ name, age, place }) {
    if(age >= 18 && person?.isPrevUser) {
        greet(name)
        suggestFriendsFrom(place)
        person.isLoggedIn = true
        return person
    }
    return null
}

login(new Person("Alice", 20, "WonderLand")

It means what you proposed it should mean. (Indirectly, but you did ;)). Shows how confusing the pattern is, if even its author can't recognize it ;)

// note that in current JS
function (PATTERN) { ... }
// works like
function (arg) { let PATTERN = arg; ... }

// your previous suggestion:
function (obj.{ a }) { ... }

// implies that it works like:
function (arg} { let obj.{ a } = arg; ... }

Hum intriguing. :grinning:

I didn't imagine initially this proposal as a left side instruction.

It would give:

let obj = { ... };
obj.{ a, b, c.{ d } } = { x, y, z }; // Object Cherry Assignment
// b = { ..., a: x, b: y, c: { ..., d: z } }

It works like a sort of xpath assignment.
You can assign very precisely and restructure the object.
Not a bad feature. :+1:

1 Like

like wise you've said:

function fx(arg.{x, y, z}) { //... }
// would be like
function fx(arg) {
    let {x, y, z} = arg
}
// why can't it be like this☝

I didn't say it can't be like that. You're missing the point. You asked what this means:

const obj.{ a } = result

I tried to explain that the meaning is embedded in the function parameter pattern you proposed. I didn't add any new idea, it's all in your definition. There is nothing special about the pattern obj.{ a } appearing in const declaration, it has to work the same as when it appears among function parameters (however you define that).

of course not! that was a rhetorical question to assert a pointπŸ˜…. That point was that if such a thing was valid; it wouldn't be of much use.

(obj.{ a, b }) => console.log(obj, a + b)
// this sort of destructuring is of some use in functions
// however
const obj.{ a, b } = { a: 1, b: 2, c: 3, d: 4 }
// according to my understanding
// this is equivalent to:
const obj = { a: 1, b: 2, c: 3, d: 4 }
const { a, b } = obj
// apart from being a short-hand for something, I don't think that's of much use, I might be wrong though!
// and I don't think it's much confusing either

@jithujoshyjy - sorry, didn't realize you only intended it to work in a function parameter list. But why are you limiting it to that? Why not have it work as any assignment target?

const [x.{ a, b }] = [{ a: 1, b: 2 }]
x // { a: 1, b: 2 }
a // 1
b // 2

It does make this version a little useless, but its consistent:

const x.{ a, b } = { a: 1, b: 2 }
x // { a: 1, b: 2 }
a // 1
b // 2

But, now we run back into the original issue I was describing, which is the fact that your proposed obj.{ a, b } behavior is not the reversal of property picking, which is why it does not make sense to use this kind of behavior in an assignment position. Even if you limit this to only work in a function parameter list, I would still expect it to strictly be the reversal of obj.{ a, b } which its not.

I agree that behavior like this is useful. It should just use different syntax, because it has nothing in common with property picking. For example, you could do fn({ x, y } as obj) { ... } as an alternative syntax, or if this property picking syntax does not go through, then I would be fine with using obj.{ x, y } at an assignment position to do what you're proposing. They just can't both coexist when they don't have anything in common.

2 Likes

Reply higher up!

That was my preliminary thought! but why not!!

This idea would feel natural to achieve the @ jithujoshyjy purpose