-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add ArrayContains decorator #192
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
base: develop
Are you sure you want to change the base?
Changes from all commits
81fd826
6551711
c3ea216
7ec8a03
ee0f1d5
361bd90
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| export function isDeepEqual(obj1: any, obj2: any): boolean { | ||
| if (obj1 === obj2) return true | ||
|
|
||
| const stack: [any, any][] = [[obj1, obj2]] | ||
| while (stack.length > 0) { | ||
| const [a, b] = stack.pop()! | ||
|
|
||
| if (a === b) continue | ||
| if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) { | ||
| return false | ||
| } | ||
|
|
||
| // Handle Date objects specifically | ||
| if (a instanceof Date && b instanceof Date) { | ||
| if (a.getTime() !== b.getTime()) return false | ||
| continue | ||
| } | ||
|
|
||
| // Check if both are arrays or neither (for accuracy and performance) | ||
| const isArrayA = Array.isArray(a) | ||
| const isArrayB = Array.isArray(b) | ||
| if (isArrayA !== isArrayB) return false | ||
|
|
||
| const keysA = Object.keys(a) | ||
| const keysB = Object.keys(b) | ||
| if (keysA.length !== keysB.length) return false | ||
|
|
||
| for (const key of keysA) { | ||
| if (!Object.prototype.hasOwnProperty.call(b, key)) return false | ||
| stack.push([a[key], b[key]]) | ||
| } | ||
| } | ||
|
|
||
| return true | ||
| } | ||
|
Comment on lines
+1
to
+35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이 문제를 해결하기 위해 이미 비교한 객체 쌍을 추적하는 메커니즘을 추가해야 합니다. export function isDeepEqual(obj1: any, obj2: any): boolean {
if (obj1 === obj2) return true
// 순환 참조를 처리하기 위해 방문한 객체 쌍을 추적합니다.
const visited = new Map<object, Set<object>>()
const stack: [any, any][] = [[obj1, obj2]]
while (stack.length > 0) {
const [a, b] = stack.pop()!
if (a === b) continue
if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
return false
}
// 이미 비교한 쌍이면 건너뜁니다.
if (visited.has(a) && visited.get(a)!.has(b)) {
continue
}
if (!visited.has(a)) {
visited.set(a, new Set())
}
visited.get(a)!.add(b)
// Date 객체를 특별히 처리합니다.
if (a instanceof Date && b instanceof Date) {
if (a.getTime() !== b.getTime()) return false
continue
}
// 두 값 모두 배열이거나 아니어야 합니다.
const isArrayA = Array.isArray(a)
const isArrayB = Array.isArray(b)
if (isArrayA !== isArrayB) return false
const keysA = Object.keys(a)
const keysB = Object.keys(b)
if (keysA.length !== keysB.length) return false
for (const key of keysA) {
if (!Object.prototype.hasOwnProperty.call(b, key)) return false
stack.push([a[key], b[key]])
}
}
return true
}
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HTTP 요청의 JSON 데이터는 직렬화된 문자열 형태로 전송되며, 표준 JSON 규격상 순환 참조를 포함할 수 없습니다. 또한, 우리 라이브러리 이전 단계인 그러므로 발생할 수 없는 케이스를 위해 모든 객체 노드를 순회하며 순환 참조를 체크하는 로직을 추가하는 것은 라이브러리의 성능에 불필요한 비용을 발생시킨다고 생각하여 해당 리뷰는 반영하지 않도록 하겠습니다. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { cargoErrorMessage, EachValidatorRule, TypedPropertyDecorator, UuidVersion, ValidatorRule } from './types' | ||
| import { CargoClassMetadata } from './metadata' | ||
| import { isDeepEqual } from './utils' | ||
|
|
||
| function addValidator(target: any, propertyKey: string | symbol, rule: ValidatorRule) { | ||
| const classMeta = new CargoClassMetadata(target) | ||
|
|
@@ -441,6 +442,56 @@ export function Without(fieldName: string, message?: cargoErrorMessage): Propert | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Checks if the array contains all the specified values. | ||
| * @param values - The values that must be present in the array. | ||
| * @param message - Optional custom error message. | ||
| */ | ||
| export function ArrayContains(values: any[], message?: cargoErrorMessage): TypedPropertyDecorator<any[]> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. equal 여부 판단을 위한 함수도 optional 로 개발자가 입력할수 있게 하면 어떨가요?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 기본적으로 저희가 제공하는 함수를 사용하되, 추가적인 옵션을 받자는 말씀이신가요? |
||
| const expectedPrimitives = values.filter(v => v === null || typeof v !== 'object') | ||
| const expectedObjects = values.filter(v => v !== null && typeof v === 'object') | ||
|
|
||
| return (target: Object, propertyKey: string | symbol): void => { | ||
| addValidator( | ||
| target, | ||
| propertyKey, | ||
| new ValidatorRule( | ||
| propertyKey, | ||
| 'arrayContains', | ||
| (value: unknown) => { | ||
| if (!Array.isArray(value)) { | ||
| return false | ||
| } | ||
| const actualPrimitiveSet = new Set() | ||
| const actualObjects: any[] = [] | ||
|
|
||
| for (const item of value) { | ||
| if (item === null || typeof item !== 'object') { | ||
| actualPrimitiveSet.add(item) | ||
| } else { | ||
| actualObjects.push(item) | ||
| } | ||
| } | ||
|
|
||
| // Verify all expected primitive values exist in the actual array | ||
| for (const req of expectedPrimitives) { | ||
| if (!actualPrimitiveSet.has(req)) return false | ||
| } | ||
|
|
||
| // Verify all expected objects exist in the actual array using deep equality | ||
| for (const reqObj of expectedObjects) { | ||
| const found = actualObjects.some(actObj => isDeepEqual(actObj, reqObj)) | ||
| if (!found) return false | ||
| } | ||
|
|
||
| return true | ||
| }, | ||
| message || `${String(propertyKey)} must contain all specified values`, | ||
| ), | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Applies validation rules to each element of an array. | ||
| * @param args - Validation decorators or functions to apply to each element. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.