Object.prototype.pick / Object.prototype.omit

Like what Lodash has implemented _.pick, _.omit:

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

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

Just a note that an omit I can sort of be done today:

const { a, ...foo } = { a: 1, b: 2 }; // => foo becomes {b : 1}

The pick one is trickier, agree that it would be very nice to have easy such methods.

pick would be Object.fromEntries(keys.map(k => [k, obj[k]])), which indeed isn't very ergonomic.

1 Like

Another problem is whether should we concern about properties from the prototype chain?

const pick = keys => Object.fromEntries(
    keys.map(k => obj.hasOwnProperty(k) && [k, obj[k]]).filter(x => x)
);

const omit = keys => Object.fromEntries(
    keys.map(k => !obj.hasOwnProperty(k) && [k, obj[k]]).filter(x => x)
);

I have created a draft proposal, and feel free to discuss it.

Yes, a bultin function should do that. And concerning prototypes, Object.prototype.pick is out of the question, Object.pick it would need to be.

Btw, with the shorthand improvements proposal you can write const o2 = { o1.a }, which I'd prefer over o2 = Object.pick(o1, ['a']) for non-dynamic properties.

2 Likes

Sometimes, if we want to ensure that an object won't has any other side-effected properties, the pick / omit method should be better.

postData({[key1] : o[key1], [key2] : o[key2]});
postData(o.pick([key1, key2]));

Is it triggered the getter or copy the property descriptor?

I disagree, Please do not define in Object.prototype

({ pick: undefined }).pick(['pick'])
({ omit: undefined }).omit(['omit'])
1 Like
Object.keys({}) // returns ["pick", "omit"]

is this expectation?

You may misunderstand:

Object.keys({ pick: undefined, omit: undefined }); // => ["pick", "omit"]
Object.keys(({ pick: undefined, omit: undefined }).pick(['pick'])); // => ["pick"]

I think this is normal

This can't work, break the web.

({ pick() { throw new Error("pick") } }).pick(["pick"])
1 Like

In that case, pick must be a method on arrays, otherwise it wouldn’t work (because the keys of that array are 0 and 1).

The second line has mismatched parenthesis, did you mean Object.keys(({…}).pick(…)) or Object.keys({…}).pick(…)? But neither of them will work with Object.prototype.pick.

I don't understand why it breaks? For instance:

const pick = (obj, keys) => Object.fromEntries(
    keys.map(k => obj.hasOwnProperty(k) && [k, obj[k]]).filter(x => x)
);

pick({
    pick() { throw new Error("pick") },
    omit: undefined,
}, ['pick']); // => {pick: f}

I have testd the implementation of Lodash, and it of course trigger the getter:

_.pick(Object.defineProperty({}, 'a', {
    get: () => 'b',
}), ['a']).a; // => "b"

For arrays:

[1, 2, 3].pick([0]); // => {0: 1}
[1, 2, 3].pick([0, 1]); // => {0: 1, 1: 2}

I DISAGREE, Re-define any object prototype!

I think such a method will only create a new object with copying some of its prototype without redefine them.

You defined in Object.prototype.pick, thanks.

const foo = { pick() { throw new Error("pick") } }

foo.pick() // here expect is throw a error, but not is `Object.prototype.pick`.