From 3d888baffaa9c7d5aa560a53bcea292d1ec3dffe Mon Sep 17 00:00:00 2001 From: Avram Walden Date: Sun, 9 Jun 2024 10:51:35 -0700 Subject: [PATCH] perf(unsetcompact): avoids recursion for trailing [] notation --- src/utils/unsetCompact.ts | 21 ++++++++++++--------- tests/utils/unsetCompact.test.ts | 22 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/utils/unsetCompact.ts b/src/utils/unsetCompact.ts index 6b165b4..42556ae 100644 --- a/src/utils/unsetCompact.ts +++ b/src/utils/unsetCompact.ts @@ -2,18 +2,21 @@ import { unset, get } from 'lodash' import { type NestedObject } from '../useInertiaForm' /** - * Extends _.unset to remove empty array elements after unsetting an array by index + * Extends _.unset splice out array elements rather than leaving empty values in arrays * e.g. unset(data, 'path[0]') * Allows special syntax of '[]' to refer to every element of an array * e.g. unset(data, 'path[].key'), will recursively unset 'key' in every array element */ type TArrType = string|number|NestedObject export const unsetCompact = (data: NestedObject, path: string) => { + // Ignore tailing [] since it causes unnecessary recursion + const sanitizedPath = path.replace(/\[\]$/, '') + // Handle special empty array syntax - if(path.includes('[]')) { - const emptyArrayPosition = path.indexOf('[]') - const startPath = path.slice(0, emptyArrayPosition) - const restPath = path.slice(emptyArrayPosition + 2) + if(sanitizedPath.includes('[]')) { + const emptyArrayPosition = sanitizedPath.indexOf('[]') + const startPath = sanitizedPath.slice(0, emptyArrayPosition) + const restPath = sanitizedPath.slice(emptyArrayPosition + 2) const arr = get(data, startPath) as TArrType[] if(Array.isArray(arr)) { @@ -26,14 +29,14 @@ export const unsetCompact = (data: NestedObject, path: string) => { // Directly removing an array element is the only way to have an empty array element // Handle it separately using slice rather than unset - if(path.charAt(path.length - 1) === ']') { - const match = path.match(/(?\d*)\]$/) - const arr = get(data, path.slice(0, path.lastIndexOf('['))) + if(sanitizedPath.charAt(sanitizedPath.length - 1) === ']') { + const match = sanitizedPath.match(/(?\d*)\]$/) + const arr = get(data, sanitizedPath.slice(0, sanitizedPath.lastIndexOf('['))) if(Array.isArray(arr) && match?.groups?.index !== undefined) { arr.splice(Number(match.groups.index), 1) } } else { - unset(data, path) + unset(data, sanitizedPath) } } diff --git a/tests/utils/unsetCompact.test.ts b/tests/utils/unsetCompact.test.ts index e2623ae..dbc9a69 100644 --- a/tests/utils/unsetCompact.test.ts +++ b/tests/utils/unsetCompact.test.ts @@ -136,7 +136,7 @@ describe('unsetCompact', () => { }) }) - it('works when an empty bracket is specified after an element ', () => { + it('works when an empty bracket is specified after an element', () => { const data = structuredClone(nestedData) unsetCompact(data, 'two.four[2].ten[].eleven') @@ -157,7 +157,27 @@ describe('unsetCompact', () => { }, }, }) + }) + + it('ignores trailing []', () => { + const data = structuredClone(nestedData) + unsetCompact(data, 'two.four[2].ten[]') + expect(data).toEqual({ + one: 'one', + two: { + three: 'three', + four: [ + { five: 'five', six: 'six' }, + { seven: 'seven' }, + { five: 'eight', six: 'nine' }, + ], + last: { + just: 'testing', + }, + }, + }) }) + }) })