Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add UniqueItems tag #1062

Open
xxmichas opened this issue Jun 1, 2024 · 5 comments
Open

add UniqueItems tag #1062

xxmichas opened this issue Jun 1, 2024 · 5 comments
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers
Projects

Comments

@xxmichas
Copy link

xxmichas commented Jun 1, 2024

A description of the problem you're trying to solve:

Currently, when defining arrays of primitive types, there is no built-in mechanism to enforce that array items are unique.

An overview of the suggested solution:

I propose adding a new tag for unique items in arrays, leveraging the uniqueItems property from the OpenAPI specification.

OpenAPI documentation on uniqueItems: Swagger Data Types - uniqueItems

I'm unsure if uniqueItems should work on object-types (or if it's even possible to implement). Name could be adjusted to indicate that it only works on primitive types (if implementing it for object types is too hard).

Code examples showing the expected behavior:

type UniqueItems = typia.tags.TagBase<{
    kind: "uniqueItems";
    target: "array";
    value: undefined;
    validate: `(new Set($input)).size === $input.length`;
    exclusive: true;
    schema: {
        uniqueItems: true;
    };
}>;

Examples of how the suggestion would work in various places:

export interface MyType {
    emails: Array<string & tags.Format<"email">> & tags.MaxItems<5> & tags.UniqueItems;
}
@samchon samchon self-assigned this Jun 3, 2024
@samchon samchon added enhancement New feature or request good first issue Good for newcomers labels Jun 3, 2024
@samchon
Copy link
Owner

samchon commented Jun 3, 2024

Do you know what uniqueItems work when the element type is object or another array?

@samchon samchon added this to To do in v6 Update via automation Jun 3, 2024
@samchon samchon closed this as completed in 76f6ed2 Jun 3, 2024
samchon added a commit that referenced this issue Jun 3, 2024
Close #1062: add `UniqueItems` type tag.
v6 Update automation moved this from To do to Done Jun 3, 2024
samchon added a commit to samchon/nestia that referenced this issue Jun 3, 2024
samchon added a commit to samchon/nestia that referenced this issue Jun 3, 2024
Close samchon/typia#1062: adapt `UniqueItems` tag on `@nestia/migrate`.
@xxmichas
Copy link
Author

xxmichas commented Jun 4, 2024

Do you know what uniqueItems work when the element type is object or another array?

I did some research, and indeed it seems that uniqueItems should validate the array items deeply (both nested objects and arrays).

Unfortunately spec is very minimalistic about uniqueItems, but one of its authors (Henry Andrews) suggested using this tag here to validate an array of objects.

I tested the following schemas using Ajv and it did validate deeply for all of them. Ajv's docs.

You can use this online playground to quickly test them out.

Array of objects

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "users": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "integer"
          },
          "username": {
            "type": "string"
          }
        },
        "required": ["userId", "username"],
        "additionalProperties": false
      },
      "uniqueItems": true
    }
  },
  "required": ["users"]
}

Valid

{
  "users": [
    {
      "userId": 1,
      "username": "user1"
    },
    {
      "userId": 2,
      "username": "user2"
    }
  ]
}

Invalid

{
  "users": [
    {
      "userId": 1,
      "username": "user1"
    },
    {
      "userId": 1,
      "username": "user1"
    }
  ]
}

Array of objects - nested

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "users": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "integer"
          },
          "username": {
            "type": "string"
          },
          "profile": {
            "type": "object",
            "properties": {
              "email": {
                "type": "string",
                "format": "email"
              },
              "age": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": ["email", "age"],
            "additionalProperties": false
          }
        },
        "required": ["userId", "username", "profile"],
        "additionalProperties": false
      },
      "uniqueItems": true
    }
  },
  "required": ["users"]
}

Valid

{
  "users": [
    {
      "userId": 1,
      "username": "user1",
      "profile": {
        "email": "user1@example.com",
        "age": 25
      }
    },
    {
      "userId": 2,
      "username": "user2",
      "profile": {
        "email": "user2@example.com",
        "age": 30
      }
    }
  ]
}

Invalid

{
  "users": [
    {
      "userId": 1,
      "username": "user1",
      "profile": {
        "email": "user1@example.com",
        "age": 25
      }
    },
    {
      "userId": 1,
      "username": "user1",
      "profile": {
        "email": "user1@example.com",
        "age": 25
      }
    }
  ]
}

Matrix

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "matrix": {
      "type": "array",
      "items": {
        "type": "array",
        "items": {
          "type": "integer"
        },
        "uniqueItems": true
      },
      "uniqueItems": true
    }
  },
  "required": ["matrix"]
}

Valid

{
  "matrix": [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
  ]
}

Invalid

{
  "matrix": [
    [1, 2, 3],
    [1, 2, 3],
    [4, 5, 6]
  ]
}

@xxmichas
Copy link
Author

xxmichas commented Jun 4, 2024

I also noticed a small issue with the current uniqueItems implementation. When false is passed into tags.UniqueItems, empty arrays and arrays with only one item are considered invalid, even though they should be valid.

@samchon
Copy link
Owner

samchon commented Jun 5, 2024

Nested object and array case, I need to make special function for them.

By the way, using external function in the type tag is not possible now. It would be supported at v7 update, so that please wait for some months about that feature. Until that, just hope to satisfy only with atomic value unique checking like string[].

@samchon samchon reopened this Jun 5, 2024
v6 Update automation moved this from Done to In progress Jun 5, 2024
samchon added a commit that referenced this issue Jun 5, 2024
samchon added a commit that referenced this issue Jun 5, 2024
Complement #1062: `UniqueItems<false>` case.
@xxmichas
Copy link
Author

xxmichas commented Jun 5, 2024

All good. Thank you for your amazing libraries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
v6 Update
In progress
Development

No branches or pull requests

2 participants