diff --git a/src/utils/set.ts b/src/utils/set.ts index ac48624c..4e004150 100644 --- a/src/utils/set.ts +++ b/src/utils/set.ts @@ -1,7 +1,10 @@ -export default function set( +import get from './get'; + +function internalSet( entity: Entity, paths: (string | number)[], value: Value, + removeIfUndefined: boolean, ): Output { if (!paths.length) { return (value as unknown) as Output; @@ -18,7 +21,31 @@ export default function set( clone = ({ ...entity } as unknown) as Output; } - clone[path] = set(clone[path], restPath, value); + // Delete prop if `removeIfUndefined` and value is undefined + if (removeIfUndefined && value === undefined && restPath.length === 1) { + delete clone[path][restPath[0]]; + } else { + clone[path] = internalSet(clone[path], restPath, value, removeIfUndefined); + } return clone; } + +export default function set( + entity: Entity, + paths: (string | number)[], + value: Value, + removeIfUndefined: boolean = false, +): Output { + // Do nothing if `removeIfUndefined` and parent object not exist + if ( + paths.length && + removeIfUndefined && + value === undefined && + !get(entity, paths.slice(0, -1)) + ) { + return (entity as unknown) as Output; + } + + return internalSet(entity, paths, value, removeIfUndefined); +} diff --git a/tests/utils.test.js b/tests/utils.test.js index 47841428..f6112df3 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -19,36 +19,83 @@ describe('utils', () => { ); }); - it('set', () => { - expect(set(null, [], 233)).toEqual(233); - expect(set({}, ['light'], 'bamboo')).toEqual({ light: 'bamboo' }); - expect(set({}, ['light', 'bamboo'], 'generate')).toEqual({ - light: { bamboo: 'generate' }, - }); - expect( - set({ other: { next: 233 } }, ['light', 'bamboo'], 'generate'), - ).toEqual({ - other: { next: 233 }, - light: { bamboo: 'generate' }, + describe('set', () => { + it('basic', () => { + expect(set(null, [], 233)).toEqual(233); + expect(set({}, ['light'], 'bamboo')).toEqual({ light: 'bamboo' }); + expect(set({}, ['light', 'bamboo'], 'generate')).toEqual({ + light: { bamboo: 'generate' }, + }); + expect( + set({ other: { next: 233 } }, ['light', 'bamboo'], 'generate'), + ).toEqual({ + other: { next: 233 }, + light: { bamboo: 'generate' }, + }); + + expect(set([0, 1, 2], [1, 'light', 'bamboo'], 'next')).toEqual([ + 0, + { light: { bamboo: 'next' } }, + 2, + ]); + expect( + set({ light: 'bamboo', list: [0, 1, 2] }, ['list', '1'], 233), + ).toEqual({ + light: 'bamboo', + list: [0, 233, 2], + }); + expect(set([[[[[0]]]]], [0, 0, 0, 0, 0], 'light')).toEqual([ + [[[['light']]]], + ]); + expect(set([[[[[0]]]]], [0, 0, 0, 0, 0, 0], 'bamboo')).toEqual([ + [[[[['bamboo']]]]], + ]); }); - expect(set([0, 1, 2], [1, 'light', 'bamboo'], 'next')).toEqual([ - 0, - { light: { bamboo: 'next' } }, - 2, - ]); - expect( - set({ light: 'bamboo', list: [0, 1, 2] }, ['list', '1'], 233), - ).toEqual({ - light: 'bamboo', - list: [0, 233, 2], + it('remove if undefined', () => { + // Skip not exist path + expect(set({}, ['notExist'], undefined, true)).toEqual({}); + + // Delete value + const target = set( + { keep: { light: 2333 } }, + ['keep', 'light'], + undefined, + true, + ); + expect(target).toEqual({ keep: {} }); + expect('light' in target.keep).toBeFalsy(); + + // Mid path not exist + const midTgt = set( + { lv1: { lv2: {} } }, + ['lv1', 'lv2', 'lv3'], + undefined, + true, + ); + expect(midTgt).toEqual({ lv1: { lv2: {} } }); + expect('lv3' in midTgt.lv1.lv2).toBeFalsy(); + + // Long path not exist + const longNotExistTgt = set( + { lv1: { lv2: {} } }, + ['lv1', 'lv2', 'lv3', 'lv4'], + undefined, + true, + ); + expect(longNotExistTgt).toEqual({ lv1: { lv2: {} } }); + expect('lv3' in longNotExistTgt.lv1.lv2).toBeFalsy(); + + // Long path remove + const longTgt = set( + { lv1: { lv2: { lv3: { lv4: { lv: 5 } } } } }, + ['lv1', 'lv2', 'lv3', 'lv4'], + undefined, + true, + ); + expect(longTgt).toEqual({ lv1: { lv2: { lv3: {} } } }); + expect('lv4' in longTgt.lv1.lv2.lv3).toBeFalsy(); }); - expect(set([[[[[0]]]]], [0, 0, 0, 0, 0], 'light')).toEqual([ - [[[['light']]]], - ]); - expect(set([[[[[0]]]]], [0, 0, 0, 0, 0, 0], 'bamboo')).toEqual([ - [[[[['bamboo']]]]], - ]); }); describe('pickAttrs', () => {