diff --git a/README.md b/README.md index af79ea7..cd373ec 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,18 @@ v.validate({ roles: ["user", "admin"] }, schema); // Valid v.validate({ roles: ["guest"] }, schema); // Fail ``` +**Example for `unique`:** +```js +const schema = { + roles: { type: "array", unique: true } +} + +v.validate({ roles: ["user"] }, schema); // Valid +v.validate({ roles: [{role:"user"},{role:"admin"},{role:"user"}] }, schema); // Valid +v.validate({ roles: ["user", "admin", "user"] }, schema); // Fail +v.validate({ roles: [1, 2, 1] }, schema); // Fail +``` + ### Properties Property | Default | Description -------- | -------- | ----------- @@ -363,6 +375,7 @@ Property | Default | Description `max` | `null` | Maximum count of elements. `length` | `null` | Fix count of elements. `contains` | `null` | The array must contain this element too. +`unique` | `null` | The array must be unique (array of objects is always unique). `enum` | `null` | Every element must be an element of the `enum` array. `items` | `null` | Schema for array items. @@ -969,6 +982,7 @@ Name | Default text `arrayMax` | The '{field}' field must contain less than or equal to {expected} items. `arrayLength` | The '{field}' field must contain {expected} items. `arrayContains` | The '{field}' field must contain the '{expected}' item. +`arrayUnique` | The '{actual}' value in '{field}' field does not unique the '{expected}' values. `arrayEnum` | The '{actual}' value in '{field}' field does not match any of the '{expected}' values. `boolean` | The '{field}' field must be a boolean. `function` | The '{field}' field must be a function. diff --git a/lib/messages.js b/lib/messages.js index 3b32ab4..5f04d08 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -31,6 +31,7 @@ module.exports = { arrayMax: "The '{field}' field must contain less than or equal to {expected} items.", arrayLength: "The '{field}' field must contain {expected} items.", arrayContains: "The '{field}' field must contain the '{expected}' item.", + arrayUnique: "The '{actual}' value in '{field}' field does not unique the '{expected}' values.", arrayEnum: "The '{actual}' value in '{field}' field does not match any of the '{expected}' values.", boolean: "The '{field}' field must be a boolean.", diff --git a/lib/rules/array.js b/lib/rules/array.js index afa1514..04da100 100644 --- a/lib/rules/array.js +++ b/lib/rules/array.js @@ -54,6 +54,14 @@ module.exports = function({ schema, messages }, path, context) { `); } + if (schema.unique === true) { + src.push(` + if(len > (new Set(value)).size) { + ${this.makeError({ type: "arrayUnique", expected: "Array.from(new Set(value.filter((item, index) => value.indexOf(item) !== index)))", actual: "value", messages })} + } + `); + } + if (schema.enum != null) { const enumStr = JSON.stringify(schema.enum); src.push(` diff --git a/test/messages.spec.js b/test/messages.spec.js index b194167..42abc71 100644 --- a/test/messages.spec.js +++ b/test/messages.spec.js @@ -30,6 +30,7 @@ describe("Test Messages", () => { expect(msg.arrayMax).toBeDefined(); expect(msg.arrayLength).toBeDefined(); expect(msg.arrayContains).toBeDefined(); + expect(msg.arrayUnique).toBeDefined(); expect(msg.arrayEnum).toBeDefined(); expect(msg.boolean).toBeDefined(); expect(msg.function).toBeDefined(); diff --git a/test/rules/array.spec.js b/test/rules/array.spec.js index c42c32c..5403530 100644 --- a/test/rules/array.spec.js +++ b/test/rules/array.spec.js @@ -71,6 +71,16 @@ describe("Test rule: array", () => { expect(check([8, 5, 2])).toEqual(true); }); + it("check unique", () => { + const check = v.compile({ $$root: true, type: "array", unique: true }); + + expect(check(["bob","john","bob"])).toEqual([{ type: "arrayUnique", expected: ["bob"], actual: ["bob", "john", "bob"], message: "The 'bob,john,bob' value in '' field does not unique the 'bob' values." }]); + expect(check(["bob","john","bob","bob","john"])).toEqual([{ type: "arrayUnique", expected: ["bob","john"], actual: ["bob","john","bob","bob","john"], message: "The 'bob,john,bob,bob,john' value in '' field does not unique the 'bob,john' values." }]); + expect(check([1,2,1,false,true,false])).toEqual([{ type: "arrayUnique", expected: [1,false], actual: [1,2,1,false,true,false], message: "The '1,2,1,false,true,false' value in '' field does not unique the '1,false' values." }]); + expect(check([{name:"bob"},{name:"john"},{name:"bob"}])).toEqual(true); + expect(check(["john", "bob"])).toEqual(true); + }); + it("check enum", () => { const check = v.compile({ $$root: true, type: "array", enum: ["male", "female"] });