-
-
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: