I have a funciton that accept a property name
func(propertyName) {
return object[propertyName];
}
so by calling func('value')
will return the object.value
However the object is complex, so it has inner props
What I want to do is to be able to do that func('property1.property2')
What is the best way to do that?
A combination of reduce
and the Optional chaining operator ensures both a fail safe implementation and a fail safe access of any passed property key path at any passed type/object ...
function getValueByKeyPath(obj, path) {
return String(path)
.split('.')
.reduce((value, key) => value?.[key], Object(obj))
}
const sampleData = {
foo: {
value: 'foo',
bar: {
value: 'bar',
baz: {
value: 'baz',
},
},
},
};
console.log(
"getValueByKeyPath(sampleData, 'foo.bar.baz') ...",
getValueByKeyPath(sampleData, 'foo.bar.baz')
);
console.log(
"getValueByKeyPath(sampleData, 'foo.bar.baz.value') ...",
getValueByKeyPath(sampleData, 'foo.bar.baz.value')
);
console.log(
"\nfail safe ... getValueByKeyPath(sampleData, 'foo.biz.baz.value') ...",
getValueByKeyPath(sampleData, 'foo.biz.baz.value')
);
console.log(
"\nfail safe ... getValueByKeyPath('', 'toString') ...",
getValueByKeyPath('', 'toString')
);
console.log(
"fail safe ... getValueByKeyPath(null, '') ...",
getValueByKeyPath(null, '')
);
console.log(
"fail safe ... getValueByKeyPath() ...",
getValueByKeyPath()
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
The above approach can be extended to bracket notation with both quoted and unquoted keys for e.g. nested/mixed object and array based data-structures. Then one does not split the key-path at simply any dot but also at any opening and closing bracket while capturing the value inside the brackets. The result of this split operation needs to be sanitized from some falsy artifacts, the reducing part stays the same ...
function getValueByKeyPath(obj, path) {
return String(path)
.split(/\.|\[['"]?([^'"\]]*)['"]?\]/)
.filter(elm => !!elm)
.reduce((value, key) => value?.[key], Object(obj))
}
const sampleDataA = {
foo: {
value: 'foo',
bar: {
value: 'bar',
baz: {
value: 'baz',
},
},
},
};
const sampleDataB = {
foo: {
bar: [{
baz: {
value: 'baz',
biz: {
value: 'biz',
},
}
}, {
buzz: {
value: 'buzz',
booz: {
value: 'booz',
},
}
}],
},
};
console.log(
"getValueByKeyPath(sampleDataA, 'foo.bar.baz.value') ...",
getValueByKeyPath(sampleDataA, 'foo.bar.baz.value')
);
console.log(
"getValueByKeyPath(sampleDataA, 'foo.bar[\"baz\"].value') ...",
getValueByKeyPath(sampleDataA, 'foo.bar["baz"].value')
);
console.log(
"getValueByKeyPath(sampleDataB, 'foo.bar[1][\"buzz\"].booz') ...",
getValueByKeyPath(sampleDataB, 'foo.bar[1]["buzz"].booz')
);
console.log(
"fail safe ... getValueByKeyPath(sampleDataB, 'foo.bar[2].buzz.booz') ...",
getValueByKeyPath(sampleDataB, 'foo.bar[2].buzz.booz')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }