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

Closed
xxmichas opened this issue Jun 1, 2024 · 7 comments
Closed

add UniqueItems tag #1062

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

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 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.
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": "[email protected]",
        "age": 25
      }
    },
    {
      "userId": 2,
      "username": "user2",
      "profile": {
        "email": "[email protected]",
        "age": 30
      }
    }
  ]
}

Invalid

{
  "users": [
    {
      "userId": 1,
      "username": "user1",
      "profile": {
        "email": "[email protected]",
        "age": 25
      }
    },
    {
      "userId": 1,
      "username": "user1",
      "profile": {
        "email": "[email protected]",
        "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
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.

@AlexRMU
Copy link
Contributor

AlexRMU commented Aug 7, 2024

Yes, ideally any types should be supported, that is, the user will be required to create a comparison function.
For primitives, you can use something like this by default:

arr.filter((v, i, a) => a.indexOf(v) === i)
new Set(arr).size === arr.length

It would also be nice to create a comparator automatically based on types.

@github-project-automation github-project-automation bot moved this to In progress in Typia v6 Update Aug 28, 2024
samchon added a commit that referenced this issue Nov 16, 2024
Close #1062: `_isUniqueItems()`, the deepinng comparison.
@samchon samchon closed this as completed in 1d0e2d7 Dec 2, 2024
@samchon
Copy link
Owner

samchon commented Dec 2, 2024

Typia v7 has completed this issue. It even distinguishes nested object or array types.

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
No open projects
Status: In progress
Development

No branches or pull requests

3 participants