Skip to content

Commit

Permalink
Merge pull request #38 from mrodrig/feat/support-array-indexes-in-path
Browse files Browse the repository at this point in the history
Support array indexes in path
  • Loading branch information
mrodrig committed Feb 25, 2024
2 parents 1190c5c + 9bccd59 commit 89b6556
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 8 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"author": "mrodrig",
"name": "doc-path",
"description": "A document path library for Node",
"version": "4.0.2",
"version": "4.1.0",
"homepage": "https://mrodrig.github.io/doc-path",
"repository": {
"type": "git",
Expand Down
44 changes: 39 additions & 5 deletions src/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,22 @@ export function evaluatePath(obj: unknown, kp: string): unknown {
const kpVal = typeof obj === 'object' && kp in obj ? (obj as Record<string, unknown>)[kp] : undefined;
const keyVal = typeof obj === 'object' && key in obj ? (obj as Record<string, unknown>)[key] : undefined;

// If there is a '.' in the key path and the key path doesn't appear in the object, recur on the subobject
if (dotIndex >= 0 && typeof obj === 'object' && !(kp in obj)) {
const { key: nextKey } = state(remaining);
const nextKeyAsInt = parseInt(nextKey);

// If there's an array at the current key in the object, then iterate over those items evaluating the remaining path
if (Array.isArray(keyVal)) {
if (Array.isArray(keyVal) && isNaN(nextKeyAsInt)) {
return keyVal.map((doc: unknown) => evaluatePath(doc, remaining));
}
// Otherwise, we can just recur
return evaluatePath(keyVal, remaining);
} else if (Array.isArray(obj)) {
const keyAsInt = parseInt(key);
if (kp === key && dotIndex === -1 && !isNaN(keyAsInt)) {
return keyVal;
}

// If this object is actually an array, then iterate over those items evaluating the path
return obj.map((doc) => evaluatePath(doc, kp));
} else if (dotIndex >= 0 && kp !== key && typeof obj === 'object' && key in obj) {
Expand Down Expand Up @@ -65,17 +72,44 @@ function _sp<T>(obj: T, kp: string, v: unknown): T {
}

if (dotIndex >= 0) {
const keyAsInt = parseInt(key);

// If there is a '.' in the key path, recur on the subdoc and ...
if (typeof obj === 'object' && obj !== null && !(key in obj) && Array.isArray(obj)) {
if (typeof obj === 'object' && obj !== null && !(key in obj) && Array.isArray(obj) && !isNaN(keyAsInt)) {

// If there's no value at obj[key] then populate an empty object
(obj as Record<string, unknown>)[key] = (obj as Record<string, unknown>)[key] ?? {};

// Continue iterating on the rest of the key path to set the appropriate value where intended and then return
_sp((obj as Record<string, unknown>)[key], remaining, v);
return obj;
} else if (typeof obj === 'object' && obj !== null && !(key in obj) && Array.isArray(obj)) {
// If this is an array and there are multiple levels of keys to iterate over, recur.
obj.forEach((doc) => _sp(doc, kp, v));
return obj;
} else if (typeof obj === 'object' && obj !== null && !(key in obj) && !Array.isArray(obj)) {
// If the current key doesn't exist yet, populate it
(obj as Record<string, unknown>)[key] = {};

const { key: nextKey } = state(remaining);
const nextKeyAsInt = parseInt(nextKey);

if (!isNaN(nextKeyAsInt)) {
// If the current key doesn't exist yet and the next key is a number (likely array index), populate an empty array
(obj as Record<string, unknown>)[key] = [];
} else {
// If the current key doesn't exist yet, populate it
(obj as Record<string, unknown>)[key] = {};
}
}
_sp((obj as Record<string, unknown>)[key], remaining, v);
} else if (Array.isArray(obj)) {
const keyAsInt = parseInt(key);

// If the object is an array and this key is an int (likely array index), then set the value directly and return
if (kp === key && dotIndex === -1 && !isNaN(keyAsInt)) {
(obj as Record<string, unknown>)[key] = v;
return obj;
}

// If this "obj" is actually an array, then we can loop over each of the values and set the path
obj.forEach((doc) => _sp(doc, remaining, v));
return obj;
Expand Down
68 changes: 68 additions & 0 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,52 @@ describe('doc-path Module', () => {

done();
});

it('should evaluate the properties within an array properly', (done) => {
doc = {
list: [{
a: 1
}, {
a: 2
}]
};

assert.deepEqual(evaluatePath(doc, 'list.a'), [1, 2]);

done();
});

it('should evaluate the property even when an array index is included in the path', (done) => {
doc = {
list: [{
a: 1
}, {
a: 2
}]
};

assert.equal(evaluatePath(doc, 'list.0.a'), 1);
assert.equal(evaluatePath(doc, 'list.1.a'), 2);
assert.equal(evaluatePath(doc, 'list.2.a'), undefined);


done();
});

it('should evaluate the property even when an array index is the last key in the path', (done) => {
doc = {
list: [{
a: 1
}, {
a: 2
}]
};

assert.deepEqual(evaluatePath(doc, 'list.0'), { a: 1 });
assert.deepEqual(evaluatePath(doc, 'list.2'), undefined);

done();
});
});

describe('setPath', () => {
Expand Down Expand Up @@ -400,5 +446,27 @@ describe('doc-path Module', () => {
assert.equal(doc.data.options.name, 'MacBook Pro 15');
done();
});

it('should set a value properly when an array index is specified in the key path', (done) => {
setPath(doc, 'list.0.a', '1');
setPath(doc, 'list.1.a', '2');
assert.deepEqual(doc, {list: [ {a: 1}, {a: 2} ]});
done();
});

it('should set a value properly when an array index is specified in the key path', (done) => {
setPath(doc, 'list.test.0.a', '1');
setPath(doc, 'list.test.1', '2');
assert.deepEqual(doc, {list: {test: [ {a: 1}, 2 ]}});
done();
});

it('should set a value properly when an array index is specified in the key path - beyond first index', (done) => {
setPath(doc, 'list.1.a', '2');
const expected = { list: [] };
(expected.list[1] as unknown) = {a: 2};
assert.deepEqual(doc, expected);
done();
});
});
});

0 comments on commit 89b6556

Please sign in to comment.