Object.{pick,omit}

Basically, Object.pick is enough for developers to extend it as pickOwn:

const pickOwn = (obj, keys) =>
    Object.pick(obj, keys.filter(obj.hasOwnProperty.bind(obj)));

Reversely, if Object.pick won't pick up properties from the prototype chain, it is meaningless as it is strict.

What do you mean by this phrase?

@senocular and @aleen42, I am convinced. I saw pick and omit as more symmetrical operations to each other, but it makes more sense to not ignore the prototype chain.

It means that Object.pick can not be extended to pick up from the prototype chain any more.

1 Like

@ljharb To put this proposal into stage 0, what is the next step can I do? Just wait for a champion to lead?

You could start by using https://github.com/tc39/template-for-proposals to make a proposal repo. Clearly outlining the problem first and foremost, discussing prior art and userland solutions, discussing current alternatives, and then discussing a proposed solution would go a long way towards convincing a delegate to champion it.

Thanks. I have created GitHub - aleen42/proposal-object-pick-or-omit: Introduce `Object.prototype.{pick,omit}` for operating properties of objects more conveniently. before and I appreciate anyone's suggestions on it.

suggest, update this repo description, now incorrect.

image

Already update, thx.

If you treat pick as a similar problem as array slicing, it makes sense that it would have a dedicated syntax for it similar to this proposal: GitHub - tc39/proposal-slice-notation

Something like

let full = { a: 1, b: 2, c: 3 }
let part = full.{a,b} // { a: 1, b: 2 }
1 Like

Though if that's an argument for anything, it's an argument for having both syntax and a built-in function.

If I want to slice with a series of dynamic keys, how the statement should be? Like this?

full.[object]
1 Like

Slice or pick?

If we chose to support picking with dynamic keys, my recommendation would be something like this:

let full = { a: 1, b: 2, c: 3, d: 4 }

const key = 'a'
console.log(full.{ [key], d }) // { a: 1, d: 4 }

const keys = ['a', 'b']
console.log(full.{ ...keys, d }) // { a: 1, b: 2, d: 4 }

Though I would rather have both a pick() function and a pick syntax, and have the function version support dynamic keys while the syntax version does not. I don't think dynamic keys is a common enough use case for the added syntax baggage.

1 Like

In my opinion, using square brackets ([]) may conform to our perception of JavaScript as what we need is a serires of keys to pick from:

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

const keys = ['a', 'b'];
({a : 1, b : 2, c : 3}).[keys[0]]; // => {a : 1}
({a : 1, b : 2, c : 3}).[...keys]; // => {a : 1, b : 2}

Unfortunately, that would make o.[a, b] very different from o?.[a, b] and o[a, b]
(just to be clear I think comma should not be an operator there, but sadly it is).

You could drop the superfluous . in-between and just use ... to distinguish slicing from indexing:

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

const keys = ['a', 'b'];
({a : 1, b : 2, c : 3})[...[keys[0]]]; // => {a : 1}
({a : 1, b : 2, c : 3})[...keys]; // => {a : 1, b : 2}

Without the dot notation, it should be confused in such a case:

({a : 1, b : 2, c : 3})['a']; // => expect {a : 1} but 1 actually
1 Like

That "expect" is clearly wrong, nothing confusing about it. I said remove the dot outside and use 3 inside instead. You omitted the dots inside, of course that's plain old indexing.

Just to clarify, are you two (@lightmare and @aleen42) proposing these bracket syntaxes in addition to the full.{a, b} syntax that @jamiebuilds proposed? Or in place of? I presume it's in place of?

I'm going to make a couple of arguments in favor of @jamiebuilds's syntax:

It's built for the most common use case of just picking pre-known properties off of an object. You don't have to use strings (but we can still add support for dynamic key if wanted. Similar to how object literals and destructuring are built for the most common use case of non-dynamic keys, but have support for dynamic keys)

It could be extended to support other nice features, such as renaming while picking the properties off, or setting default values if the properties don't exist. Hopefully, you can see a parallel with object destructuring. With object destructuring, we pick properties off and put them in the local namespace. With this pick syntax, we pick properties off and put them in a new object.

It's actually got a minor prior art in Microsoft's Bosque language as well (see here), which has an object pick syntax that's exactly the same.

(I've actually started a thread in the past which suggested having this kind of syntax here, which is why I like it so much)

// pick a, b, and c off
// if b doesn't exist, set it to 2
// rename c to C
const newObj = oldObj.{ a, b = 2, c: C }

// Example of dynamic keys
// (again, do you see the parallel with object destructuring?)
const anotherNewObj = something.{ x, [key1], [key2]: renamedKey }
1 Like

So what should that returns?

({a : 1, b : 2, c : 3})[...['a']];

It seems like a shortcut of:

const {a, b = 2, c : C} = oldObj;
const newObj = {a, b, C};