Skip to content

Commit d5e30a2

Browse files
committed
Implemented refineNoDuplicates helper function
1 parent 3645c6f commit d5e30a2

File tree

2 files changed

+53
-29
lines changed

2 files changed

+53
-29
lines changed

lib/fields/fields.ts

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ import {
1313
import { Merge, Simplify } from "type-fest"
1414
import * as z from "zod"
1515

16-
import { NullPartialDeep } from "types/util"
16+
import { GetElementType, NullPartialDeep } from "types/util"
1717

18-
import { transformStringToNumber, utcDateZodString } from "./utils"
18+
import {
19+
refineNoDuplicates,
20+
transformStringToNumber,
21+
utcDateZodString,
22+
} from "./utils"
1923

2024
const customErrorMap: z.ZodErrorMap = (issue, ctx) => {
2125
if (
@@ -253,33 +257,14 @@ const shortcutSchema = z
253257
}
254258
}
255259
})
256-
.superRefine((arg, ctx) => {
257-
const codes = arg.map((v) => v.code)
258-
259-
const duplicateIndexes = codes.reduce((dupeMap, code, i) => {
260-
if (!code) {
261-
return dupeMap
262-
}
263-
264-
const duplicates = dupeMap.get(code)
265-
if (duplicates) {
266-
dupeMap.set(code, [...duplicates, i])
267-
} else if (codes.lastIndexOf(code) !== i) {
268-
dupeMap.set(code, [i])
269-
}
270-
return dupeMap
271-
}, new Map<string, number[]>())
272-
273-
for (const duplicate of Array.from(duplicateIndexes.entries())) {
274-
for (const duplicateIndex of duplicate[1]) {
275-
ctx.addIssue({
276-
code: "custom",
277-
message: "No duplicates",
278-
path: [duplicateIndex, "code"],
279-
})
280-
}
281-
}
282-
})
260+
.superRefine((arg, ctx) =>
261+
refineNoDuplicates(
262+
arg,
263+
ctx,
264+
"code",
265+
(v: GetElementType<typeof arg>) => v.code,
266+
),
267+
)
283268
.pipe(
284269
z
285270
.object({

lib/fields/utils.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,42 @@ export const deepNullPartialifyWithIds = <
111111
export const utcDateZodString = z
112112
.string()
113113
.regex(/\d{4}-[01]\d-[0-3]\d/, "Date is invalid.") //UTC Date without time
114+
115+
export const refineNoDuplicates = <
116+
T,
117+
TProperty extends string | number | undefined | null,
118+
>(
119+
arg: Array<T>,
120+
ctx: z.RefinementCtx,
121+
propertyPath: string,
122+
duplicateAccessorFn: (item: T) => TProperty,
123+
) => {
124+
const values = arg.map(duplicateAccessorFn)
125+
126+
const duplicateIndexes: Map<TProperty, number[]> = values.reduce(
127+
(dupeMap, value, i) => {
128+
if (!value) {
129+
return dupeMap
130+
}
131+
132+
const duplicates = dupeMap.get(value)
133+
if (duplicates) {
134+
dupeMap.set(value, [...duplicates, i])
135+
} else if (values.lastIndexOf(value) !== i) {
136+
dupeMap.set(value, [i])
137+
}
138+
return dupeMap
139+
},
140+
new Map<TProperty, number[]>(),
141+
)
142+
143+
for (const duplicate of Array.from(duplicateIndexes.entries())) {
144+
for (const duplicateIndex of duplicate[1]) {
145+
ctx.addIssue({
146+
code: "custom",
147+
message: "No duplicates",
148+
path: [duplicateIndex, propertyPath],
149+
})
150+
}
151+
}
152+
}

0 commit comments

Comments
 (0)