-
-
Save olayemii/948e5f611ee3f83e811d0040a2f0c4d4 to your computer and use it in GitHub Desktop.
| const getObjectProperty = (obj, path, defaultValue="", returnUndefined=true) => { | |
| const checkForDefaultValue = value => | |
| value !== undefined ? value : undefined; | |
| if (path === undefined) { | |
| return obj; | |
| } | |
| try { | |
| const value = path.split('.').reduce((o, i) => o[i], obj); | |
| if (value === undefined && returnUndefined) return value; | |
| return value !== undefined ? value : checkForDefaultValue(defaultValue); | |
| } catch (e) { | |
| if (e instanceof TypeError) return checkForDefaultValue(defaultValue); | |
| throw e; | |
| } | |
| }; | |
| /* | |
| const a = { | |
| b: { | |
| c: [ | |
| { | |
| d: { | |
| e: 14 | |
| } | |
| }, | |
| [16, 11] | |
| ], | |
| d: 12 | |
| } | |
| }; | |
| getObjectProperty(a, 'b.d', "Default!"); | |
| // 12 | |
| getObjectProperty(a, 'b.e', "Not a property"); | |
| // undefined | |
| getObjectProperty(a, 'b.e', "Not a property", false); | |
| // Not a property | |
| getObjectProperty(a, 'b.c.0.d.e', "Not a property!"); | |
| // 14 | |
| getObjectProperty(a, 'b.c.1.0', "Not a property!"); | |
| //16 | |
| */ |
Thanks @Just4Ease , @ahkohd :-)
@olayemii
I wrote some tests for the method, but the last test did not pass until I have to modify your code to check if it's trying to return undefined.
UPDATE SNIPPET:
const getObjectProperty = (obj, path, defaultValue) => {
const checkForDefaultValue = value =>
value !== undefined ? value : undefined;
if (path === undefined) {
return obj;
}
try {
const value = path.split('.').reduce((o, i) => o[i], obj);
return value !== undefined ? value : checkForDefaultValue(defaultValue);
} catch (e) {
if (e instanceof TypeError) return checkForDefaultValue(defaultValue);
throw e;
}
};
export default getObjectProperty;Oh yes, the tests I wrote:
describe('getObjectProperty', () => {
const testObject = { a: 'A', b: 'B', c: [1, 2] };
it('should check if property `a` exists', () => {
const test = getObjectProperty(testObject, 'a');
expect(test).toBe('A');
});
it('should check if property `d` exists', () => {
const test = getObjectProperty(testObject, 'd');
expect(test).toBeUndefined();
});
it('should check if property `c.0` exists', () => {
const test = getObjectProperty(testObject, 'c.0');
expect(typeof test).toBe('number');
expect(test).toBe(1);
});
it('should check if property `c` does not exist.', () => {
const test = getObjectProperty(testObject, 'c', 'Not Found');
expect(Array.isArray(test)).toBe(true);
expect(test).not.toBe('Not Found');
});
it(`should check if property 'd' exists, returns default value if it doesn't`, () => {
const test = getObjectProperty(testObject, 'd', 'Not Found');
expect(typeof test).toBe('string');
expect(test).toBe('Not Found');
});
});Oh yes, the tests I wrote:
describe('getObjectProperty', () => { const testObject = { a: 'A', b: 'B', c: [1, 2] }; it('should check if property `a` exists', () => { const test = getObjectProperty(testObject, 'a'); expect(test).toBe('A'); }); it('should check if property `d` exists', () => { const test = getObjectProperty(testObject, 'd'); expect(test).toBeUndefined(); }); it('should check if property `c.0` exists', () => { const test = getObjectProperty(testObject, 'c.0'); expect(typeof test).toBe('number'); expect(test).toBe(1); }); it('should check if property `c` does not exist.', () => { const test = getObjectProperty(testObject, 'c', 'Not Found'); expect(Array.isArray(test)).toBe(true); expect(test).not.toBe('Not Found'); }); it(`should check if property 'd' exists, returns default value if it doesn't`, () => { const test = getObjectProperty(testObject, 'd', 'Not Found'); expect(typeof test).toBe('string'); expect(test).toBe('Not Found'); }); });
Hmm, @ahkohd I see, but in this case the value of testObject.d is actually undefined 🤔 Should the default value really suffice when it's not trying to read a property from undefined?
@olayemii
The issue is that this line of code can result to undefined and since you used return, undefined will be returned.
return path.split(".").reduce((o, i) => o[i], obj);Try this in your browser's console with your original function:
const testObject = { a: 'A', b: 'B', c: [1, 2] };
getObjectProperty(testObject, 'd', 'Not Found');
// results in undefined instead of 'No Found'getObjectProperty(testObject, 'd', 'Not Found');
I feel this should actually return undefined, because testObject.d is actually having a value of undefined
My initial idea was that this
return path.split(".").reduce((o, i) => o[i], obj);
returns an undefined except when we are trying to read from undefined like undefined.name that is when the catch block gets invoked and a defaultValue can be used.
Very well, for my use case the later function works. Maybe you should provide a flag.
Very well, for my use case the later function works. Maybe you should provide a flag.
@ahkohd okay, I added a flag to allow undefined returns or fall back to a set default value, I also included your example. 👌🏾
Great!
Let's clarify usage: