diff --git a/src/compat/array/invokeMap.spec.ts b/src/compat/array/invokeMap.spec.ts index 03d2ccde7f..f6fba8d84d 100644 --- a/src/compat/array/invokeMap.spec.ts +++ b/src/compat/array/invokeMap.spec.ts @@ -162,7 +162,7 @@ describe('invokeMap', () => { }; const aEmptyDotC = invokeMap([objectWithEmptyPart], 'a..c'); - expect(aEmptyDotC[0]).toBeUndefined(); + expect(aEmptyDotC[0]).toBe(objectWithEmptyPart.a['']); }); it('should match the type of lodash', () => { diff --git a/src/compat/object/get.spec.ts b/src/compat/object/get.spec.ts index 37a3e798a3..abbcef5776 100644 --- a/src/compat/object/get.spec.ts +++ b/src/compat/object/get.spec.ts @@ -164,4 +164,8 @@ describe('get', () => { const object = { '-0': undefined }; expect(get(object, Object(-0), 'defaultValue')).toBe('defaultValue'); }); + + it('should read empty-string keys produced by consecutive dots', () => { + expect(get({ a: { '': { b: 1 } } }, 'a..b')).toBe(1); + }); }); diff --git a/src/compat/util/toPath.spec.ts b/src/compat/util/toPath.spec.ts index 94b7d9b5f2..8c94ba4317 100644 --- a/src/compat/util/toPath.spec.ts +++ b/src/compat/util/toPath.spec.ts @@ -48,6 +48,18 @@ describe('toPath function', () => { expect(result).toEqual(['a', '', 'b']); }); + it('keeps empty segments for consecutive dots', () => { + expect(toPath('a..b')).toEqual(['a', '', 'b']); + expect(toPath('a...b')).toEqual(['a', '', '', 'b']); + expect(toPath('..a')).toEqual(['', '', 'a']); + expect(toPath('a.b..c.d')).toEqual(['a', 'b', '', 'c', 'd']); + }); + + it('keeps a trailing empty segment for a trailing dot', () => { + expect(toPath('a.')).toEqual(['a', '']); + expect(toPath('a.b.')).toEqual(['a', 'b', '']); + }); + it('should match the type of lodash', () => { expectTypeOf(toPath).toEqualTypeOf(); }); diff --git a/src/compat/util/toPath.ts b/src/compat/util/toPath.ts index 9f48546360..9442fdaab9 100644 --- a/src/compat/util/toPath.ts +++ b/src/compat/util/toPath.ts @@ -40,10 +40,10 @@ export function toPath(deepKey: any): string[] { let quoteChar = ''; let bracket = false; - // Leading dot + // Leading dot. Record the empty segment that precedes it, but leave the dot for the + // main loop so it is still treated as a separator (e.g. '..a' -> ['', '', 'a']). if (deepKey.charCodeAt(0) === 46) { result.push(''); - index++; } while (index < length) { @@ -85,6 +85,13 @@ export function toPath(deepKey: any): string[] { result.push(key); key = ''; } + // A dot that is directly followed by another dot or the end of the string + // separates an empty segment, matching lodash (e.g. 'a..b' -> ['a', '', 'b'], + // 'a.' -> ['a', '']). + const next = deepKey[index + 1]; + if (next === undefined || next === '.') { + result.push(''); + } } else { key += char; }