Repository: jquense/yup
Pull request #1118 (2020-11-19 16:35:36)

feat: add object.pick and object.omit


Patch:
diff --git a/README.md b/README.md
index 47f5d47..5973acd 100644
--- a/README.md
+++ b/README.md
@@ -1152,6 +1152,36 @@ object({
 });
 ```
 
+#### `object.pick(keys: string[]): Schema`
+
+Create a new schema from a subset of the original's fields.
+
+```js
+const person = object({
+  age: number().default(30).required(),
+  name: string().default('pat').required(),
+  color: string().default('red').required(),
+});
+
+const nameAndAge = person.pick(['name', 'age']);
+nameAndAge.default(); // => { age: 30, name: 'pat'}
+```
+
+#### `object.omit(keys: string[]): Schema`
+
+Create a new schema with fields omitted.
+
+```js
+const person = object({
+  age: number().default(30).required(),
+  name: string().default('pat').required(),
+  color: string().default('red').required(),
+});
+
+const nameAndAge = person.omit('color']);
+nameAndAge.default(); // => { age: 30, name: 'pat'}
+```
+
 #### `object.from(fromKey: string, toKey: string, alias: boolean = false): Schema`
 
 Transforms the specified key to a new key. If `alias` is `true` then the old key will be left.
diff --git a/src/object.js b/src/object.js
index cbb5162..30080cd 100644
--- a/src/object.js
+++ b/src/object.js
@@ -261,6 +261,29 @@ inherits(ObjectSchema, MixedSchema, {
     return next;
   },
 
+  pick(keys) {
+    const picked = {};
+    for (const key of keys) {
+      if (this.fields[key]) picked[key] = this.fields[key];
+    }
+
+    return this.clone().withMutation((next) => {
+      next.fields = {};
+      return next.shape(picked);
+    });
+  },
+
+  omit(keys) {
+    const next = this.clone();
+    const fields = next.fields;
+    next.fields = {};
+    for (const key of keys) {
+      delete fields[key];
+    }
+
+    return next.withMutation((next) => next.shape(fields));
+  },
+
   from(from, to, alias) {
     let fromGetter = getter(from, true);
 


Test patch:
diff --git a/test/object.js b/test/object.js
index 18cf673..2feefb8 100644
--- a/test/object.js
+++ b/test/object.js
@@ -468,7 +468,7 @@ describe('Object types', () => {
       let inst = lazy((_, options) => {
         options.should.equal(opts);
         done();
-        return string();
+        return object();
       });
 
       inst.cast({ nested: 'foo' }, opts);
@@ -863,15 +863,6 @@ describe('Object types', () => {
     expect(inst.nullable().cast(null)).to.equal(null);
   });
 
-  // it('should camelCase with leading underscore', () => {
-  //   let inst = object().camelCase()
-  //
-  //   inst
-  //     .cast({ CON_STAT: 5, __isNew: true, __IS_FUN: true })
-  //     .should
-  //     .eql({ conStat: 5, __isNew: true, __isFun: true })
-  // })
-
   it('should CONSTANT_CASE keys', () => {
     let inst = object()
       .shape({
@@ -888,6 +879,42 @@ describe('Object types', () => {
     expect(inst.nullable().cast(null)).to.equal(null);
   });
 
+  it('should pick', async () => {
+    let inst = object({
+      age: number().default(30).required(),
+      name: string().default('pat').required(),
+      color: string().default('red').required(),
+    });
+
+    expect(inst.pick(['age', 'name']).default()).to.eql({
+      age: 30,
+      name: 'pat',
+    });
+
+    expect(
+      await inst.pick(['age', 'name']).validate({ age: 24, name: 'Bill' }),
+    ).to.eql({
+      age: 24,
+      name: 'Bill',
+    });
+  });
+
+  it('should omit', async () => {
+    let inst = object({
+      age: number().default(30).required(),
+      name: string().default('pat').required(),
+      color: string().default('red').required(),
+    });
+
+    expect(inst.omit(['age', 'name']).default()).to.eql({
+      color: 'red',
+    });
+
+    expect(
+      await inst.omit(['age', 'name']).validate({ color: 'mauve' }),
+    ).to.eql({ color: 'mauve' });
+  });
+
   xit('should handle invalid shapes better', async () => {
     var schema = object().shape({
       permissions: undefined,