From 8f8c9b08b942dcd3b60c05e42024afc176e295ac Mon Sep 17 00:00:00 2001 From: Avram Walden Date: Fri, 7 Jun 2024 17:30:31 -0700 Subject: [PATCH] feat: working on unset algorithm --- src/utils.ts | 31 +++++++++++- tests/utils.test.ts | 121 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 139 insertions(+), 13 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 5443abd..3647dd7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,10 @@ import React from 'react' import { isPlainObject, unset, get, set, isEmpty } from 'lodash' import { type NestedObject } from './useInertiaForm' +/** + * Creates context with simplified type notations + * Wraps useContext hook in an error check to enforce context context + */ export const createContext = () => { const context = React.createContext(null) @@ -18,15 +22,38 @@ export const createContext = () => { return [useContext, context.Provider] as const } +/** + * Extends _.unset to remove empty array elements after unsetting an array by index + * 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) => { + const emptyArrayPosition = path.indexOf('[].') + if(emptyArrayPosition >= 0) { + console.log({ emptyArrayPosition }) + const restPath = path.slice(emptyArrayPosition + 3) + const arr = get(data, restPath) as TArrType[] + + arr.forEach((el, i) => { + // @ts-ignore + unsetCompact(el, restPath) + arr[i] = el + }) + console.dir({ data, restPath, arr }, { depth: null }) + set(data, restPath, arr.filter(a => a)) + } + unset(data, path) let position = path.indexOf('[') - while(position >= 0) { + + if(position >= 0) { const arrPath = path.slice(0, position) - // @ts-ignore - No way to tell TS that this will be an array const arr = get(data, arrPath) as TArrType[] + + set(data, arrPath, arr.filter(a => a)) position = path.indexOf('[', position + 1) diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 8a4deeb..483b7bb 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -6,10 +6,14 @@ const nestedData: NestedObject = { two: { three: 'three', four: [ - { five: 'five' }, - { six: 'six' }, + { five: 'five', six: 'six' }, + { seven: 'seven' }, + { five: 'eight', six: 'nine', ten: [ + { eleven: 'eleven', twelve: 'twelve' }, + { eleven: 'eleven', thirteen: 'thirteen' }, + ] }, ], - seven: { + last: { just: 'testing', }, }, @@ -22,36 +26,131 @@ describe('unsetCompact', () => { unsetCompact(data, 'one') unsetCompact(data, 'two.three') - expect(data).toMatchObject({ + expect(data).toEqual({ two: { four: [ - { five: 'five' }, - { six: 'six' }, + { five: 'five', six: 'six' }, + { seven: 'seven' }, + { five: 'eight', six: 'nine', ten: [ + { eleven: 'eleven', twelve: 'twelve' }, + { eleven: 'eleven', thirteen: 'thirteen' }, + ] }, ], - seven: { + last: { just: 'testing', }, }, }) }) - it('should reorder arrays making all elements sequential', () => { + it('should reorder arrays making all elements sequential and removing empty array elements', () => { const data = structuredClone(nestedData) unsetCompact(data, 'two.four[0]') - expect(data).toMatchObject({ + unsetCompact(data, 'two.four[1].ten[0]') + + expect(data).toEqual({ one: 'one', two: { three: 'three', four: [ - { six: 'six' }, + { seven: 'seven' }, + { five: 'eight', six: 'nine', ten: [ + { eleven: 'eleven', thirteen: 'thirteen' }, + ] }, ], - seven: { + last: { just: 'testing', }, }, }) }) + + describe('recursively unsets array elements by key with empty array brackets', () => { + const data = structuredClone(nestedData) + + it('unsets all instances of a key', () => { + unsetCompact(data, 'two.four[].five') + expect(data).toEqual({ + one: 'one', + two: { + three: 'three', + four: [ + { six: 'six' }, + { seven: 'seven' }, + { six: 'nine', ten: [ + { eleven: 'eleven', twelve: 'twelve' }, + { eleven: 'eleven', thirteen: 'thirteen' }, + ] }, + ], + last: { + just: 'testing', + }, + }, + }) + }) + + it('works with nested array objects', () => { + unsetCompact(data, 'two.four[].ten[].twelve') + expect(data).toEqual({ + one: 'one', + two: { + three: 'three', + four: [ + { six: 'six' }, + { seven: 'seven' }, + { six: 'nine', ten: [ + { eleven: 'eleven' }, + { eleven: 'eleven', thirteen: 'thirteen' }, + ] }, + ], + last: { + just: 'testing', + }, + }, + }) + }) + + it('works when an element is specified after an empty bracket', () => { + unsetCompact(data, 'two.four[].ten[1].thirteen') + expect(data).toEqual({ + one: 'one', + two: { + three: 'three', + four: [ + { six: 'six' }, + { seven: 'seven' }, + { six: 'nine', ten: [ + { eleven: 'eleven' }, + { eleven: 'eleven' }, + ] }, + ], + last: { + just: 'testing', + }, + }, + }) + }) + + it('works when an empty bracket is specified after an element ', () => { + unsetCompact(data, 'two.four[2].ten[].eleven') + expect(data).toEqual({ + one: 'one', + two: { + three: 'three', + four: [ + { six: 'six' }, + { seven: 'seven' }, + { six: 'nine', ten: [] }, + ], + last: { + just: 'testing', + }, + }, + }) + + }) + }) }) describe('fillEmptyValues', () => {