From 20d2f7e0c8e10923d3e2d821313733dc2ca26979 Mon Sep 17 00:00:00 2001 From: streamich Date: Fri, 27 Jun 2025 19:28:33 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20allow=20numeric=20steps?= =?UTF-8?q?=20when=20retrieving=20array=20elemetn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/get.spec.ts | 79 +++++++++++++++++++++++++++++++++++++++ src/get.ts | 11 +++--- 2 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 src/__tests__/get.spec.ts diff --git a/src/__tests__/get.spec.ts b/src/__tests__/get.spec.ts new file mode 100644 index 0000000..61395bf --- /dev/null +++ b/src/__tests__/get.spec.ts @@ -0,0 +1,79 @@ +import {get} from '../get'; +import {test, expect} from 'vitest'; +import {parseJsonPointer} from '../util'; + +test('can find number root', () => { + const res = get(123, []); + expect(res).toBe(123); +}); + +test('can find string root', () => { + const res = get('foo', []); + expect(res).toBe('foo'); +}); + +test('can find key in object', () => { + const res = get({foo: 'bar'}, ['foo']); + expect(res).toBe('bar'); +}); + +test('can retrieve withing deep object', () => { + const res = get({foo: {bar: {baz: 'qux', a: 1}}}, ['foo', 'bar', 'baz']); + expect(res).toEqual('qux'); +}); + +test('simple key in simple object', () => { + const doc = {a: 123}; + const path = parseJsonPointer('/a'); + const res = get(doc, path); + expect(res).toEqual(123); +}); + +test('returns "undefined" when referencing missing key with multiple steps', () => { + const doc = {a: 123}; + const path = parseJsonPointer('/b/c'); + expect(get(doc, path)).toBe(undefined); +}); + +test('can reference array element', () => { + const doc = {a: {b: [1, 2, 3]}}; + const path = parseJsonPointer('/a/b/1'); + const res = get(doc, path); + expect(res).toEqual(2); +}); + +test('returns "undefined" when referencing end of array', () => { + const doc = {a: {b: [1, 2, 3]}}; + const path = parseJsonPointer('/a/b/-'); + const res = get(doc, path); + expect(res).toBe(undefined); +}); + +test('returns undefined when pointing past array boundary', () => { + const doc = {a: {b: [1, 2, 3]}}; + const path = parseJsonPointer('/a/b/-1'); + expect(get(doc, path)).toBe(undefined); +}); + +test('missing object key returns undefined', () => { + const doc = {foo: 123}; + const path = parseJsonPointer('/bar'); + const res = get(doc, path); + expect(res).toBe(undefined); +}); + +test('can reference array element by number step', () => { + const doc = [1, 2, 3]; + expect(get(doc, [0])).toBe(1); + expect(get(doc, [1])).toBe(2); + expect(get(doc, [2])).toBe(3); + expect(get(doc, [3])).toBe(undefined); +}); + +test('can reference array element by number step', () => { + const doc = {foo: {bar: [1, 2, 3]}}; + expect(get(doc, ['foo', 'bar', 0])).toBe(1); + expect(get(doc, ['foo', 'bar', 1])).toBe(2); + expect(get(doc, ['foo', 'bar', 2])).toBe(3); + expect(get(doc, ['foo', 'bar', 3])).toBe(undefined); +}); diff --git a/src/get.ts b/src/get.ts index 2ba4447..abd3549 100644 --- a/src/get.ts +++ b/src/get.ts @@ -8,11 +8,12 @@ export const get = (val: unknown, path: Path): unknown | undefined => { for (let i = 0; i < pathLength; i++) { key = path[i]; if (val instanceof Array) { - if (key === '-') return undefined; - const key2 = ~~key; - if ('' + key2 !== key) return undefined; - key = key2; - if (key < 0) return undefined; + if (typeof key !== 'number') { + if (key === '-') return undefined; + const key2 = ~~key; + if ('' + key2 !== key) return undefined; + key = key2; + } val = val[key]; } else if (typeof val === 'object') { if (!val || !has(val as object, key as string)) return undefined;