Reputation: 26783
var arr = { foo : 1, bar: { baz : 2 }, bee : 3 }
function getter(variable) {
return arr[variable];
}
If I want 'foo' vs 'bee' I can just do arr[variable]
- that's easy, and the function does that.
But what if I want to get arr.bar.baz
AKA arr[bar][baz]
?
What can I pass to the getter function that will let me do that, (and of course also let me get non-nested properties using the same function).
I tried getter('bar.baz')
and getter('[bar][baz]')
but those didn't work.
I suppose I can parse for dots or brackets (like here: In javascript, test for property deeply nested in object graph?). Is there a cleaner way? (Besides eval of course.)
Especially because I need to get the deeply set properly many many times in a loop for a bunch of array elements.
Upvotes: 61
Views: 56083
Reputation: 1
This is my first answer in stack overflow hope this goes well :)
So i was facing a similar issue. In my case it was like i have and array of objects and an array of keys for which i need to get the values as array.
For example following is the array of objects
let arr = [{ name: "Lanister",
address: {
houseNumber: 34,
street: "Casterly Rock",
pin: "123456",
},
phone: "37232112221"
},{
name: "Jon Snow",
address: {
houseNumber: 34,
street: "Winterfell",
pin: "876543",
},
phone: "23423432"
}
];
Following is the array of keys for which i need the values.
["name", "address.street", "address.pin"]
Following function will take both the parameters and return the value of arrays.
const generateTableData = (listData, displayItems = []) => {
let dataList = listData.map((item) => {
let resArr = displayItems.map((dis) => {
let nestedEle = dis.split(".");
let candidate = item;
let finalRes = null;
for(let i = 0; i <= nestedEle.length-1; i++) {
candidate = candidate[nestedEle[i]];
if(candidate !== undefined) {
finalRes = candidate;
} else {
break;
}
}
return finalRes;
});
return resArr;
});
return dataList;
}
Consoled result follows
[
[ 'Lanister', 'Casterly Rock', '123456' ],
[ 'Jon Snow', 'Winterfell', '876543' ]
]
Upvotes: 0
Reputation: 111
Others have described elegant solutions for reading deeply nested parameters, but you can also edit them by the miracle of recursion:
let myObj = {
"foo": {
"bar": {
"fee": {
"fi": {
"foe": "fum"
}
}
}
}
}
let myPath = "foo.bar.fee.fi.foe";
let myValue = "I smell the blood of an englishman";
setObjectPropertyByDynamicPath(myObj, myPath, myValue);
console.log(myObj);
function setObjectPropertyByDynamicPath(object, path, value){
let splitMyPath = path.split(".");
if(splitMyPath.length >= 1){
return sOPBDPHelper(object, splitMyPath, value);
}else{
console.error("Bad Split Path: " + splitMyPath.toString())
return object;
}
}
function sOPBDPHelper(object, pathArray, value){
if(pathArray.length == 1){
object[pathArray[0]] = value;
return object;
}else{
object[pathArray[0]] = sOPBDPHelper(object[pathArray[0]], pathArray.slice(1, pathArray.length), value);
return object;
}
}
It requires neither eval() nor any libraries.
Upvotes: 1
Reputation: 9
Super simple implementation to trace value from deeply nested object dynamically.
const exampleObj = {
collection: {
info: {
name: 'my name'
}
}
}
function getDeepObjectValue(obj, key) {
const words = key.split('.');
for (var word of words) {
if (obj[word]) {
obj = obj[word]
}
}
return obj
}
console.log(getDeepObjectValue(exampleObj, "collection.info.name"))
console.log(getDeepObjectValue(exampleObj, "collection.info"))
Upvotes: 0
Reputation: 435
Here's a very simple one liner which grants you dynamic access via "foo.bar.baz" mechanism,
var obj = {
foo: {
bar: {
baz: 'foobarbaz'
}
}
}
const nestedAccess = "foo.bar.baz";
console.log(nestedAccess.split('.').reduce((prev, cur) => prev[cur], obj)) //'foobarbaz'
Upvotes: 8
Reputation: 123
Here I created a small suite of functions to 'get / 'set' / 'push' / 'pull' from object nested properties.
inputObject : Target object. Ex: obj = {a:1, b:{c:2,d:3}}
propertyString : String containing the key to access. Ex: "b.c"
Finally:
_getObjectValueByPathString(obj, "b.c") would return 2
function _getObjectValueByPathString(inputObject, propertyString) {
let splitStr = propertyString.split('.');
if (!inputObject.hasOwnProperty(splitStr[0])) return undefined;
if (splitStr.length === 1) {
return inputObject[splitStr[0]];
}
else if (splitStr.length > 1) {
let newPropertyString = "";
let firstValue = splitStr.shift();
splitStr.forEach((subStr, i) => {
newPropertyString = i === 0 ? subStr : newPropertyString.concat(`.${subStr}`);
});
return _getObjectValueByPathString(inputObject[firstValue], newPropertyString);
}
else {
throw "Invalid property string provided";
}
}
function _setObjectValueByPathString(inputObject, propertyString, inputValue) {
let splitStr = propertyString.split('.');
if (splitStr.length === 1) {
inputObject[splitStr[0]] = inputValue;
return;
}
else if (splitStr.length > 1) {
let newPropertyString = "";
let firstValue = splitStr.shift();
splitStr.forEach((subStr, i) => {
newPropertyString = i === 0 ? subStr : newPropertyString.concat(`.${subStr}`);
});
_setObjectValueByPathString(inputObject[firstValue], newPropertyString, inputValue);
return;
}
else {
throw "Invalid property string provided";
}
}
function _pushObjectValueByPathString(inputObject, propertyString, inputValue) {
let splitStr = propertyString.split('.');
if (splitStr.length === 1) {
inputObject[splitStr[0]].push(inputValue);
return;
}
else if (splitStr.length > 1) {
let newPropertyString = "";
let firstValue = splitStr.shift();
splitStr.forEach((subStr, i) => {
newPropertyString = i === 0 ? subStr : newPropertyString.concat(`.${subStr}`);
});
_pushObjectValueByPathString(inputObject[firstValue], newPropertyString, inputValue);
return;
}
else {
throw "Invalid property string provided";
}
}
function _pullObjectValueByPathString(inputObject, propertyString, inputValue) {
let splitStr = propertyString.split('.');
if (splitStr.length === 1) {
inputObject[splitStr[0]].pull(inputValue);
return;
}
else if (splitStr.length > 1) {
let newPropertyString = "";
let firstValue = splitStr.shift();
splitStr.forEach((subStr, i) => {
newPropertyString = i === 0 ? subStr : newPropertyString.concat(`.${subStr}`);
});
_pullObjectValueByPathString(inputObject[firstValue], newPropertyString, inputValue);
return;
}
else {
throw "Invalid property string provided";
}
}
Upvotes: 0
Reputation: 151
Using reduce we can fetch the value in single line of code.
const testobj = {b:{c:'1', d:{e:'2',f:'3'}}, g:{h:'3'}}
function fetchByDotOperator(object, value) {
return value.split('.').reduce((acc, curr) => acc[curr], object);
}
console.log(fetchByDotOperator(testobj,'b.d.e'))
Upvotes: 10
Reputation: 19
let obj = {foo : {bar: {baz:1}}};
// -- simply
console.log(eval('obj.foo.bar.baz')); //-- 1
// -- safer
val = "";
try {
val = eval('Obj.foo.bar.baz')
}
catch(e) {
val = "empty"
}
// -- val = 1
// -- use at your risk ;)
Upvotes: 0
Reputation: 147513
You can use a deep access function based on a string for the path. Note that you can't have any periods in the property names.
function getPropByString(obj, propString) {
if (!propString)
return obj;
var prop, props = propString.split('.');
for (var i = 0, iLen = props.length - 1; i < iLen; i++) {
prop = props[i];
var candidate = obj[prop];
if (candidate !== undefined) {
obj = candidate;
} else {
break;
}
}
return obj[props[i]];
}
var obj = {
foo: {
bar: {
baz: 'x'
}
}
};
console.log(getPropByString(obj, 'foo.bar.baz')); // x
console.log(getPropByString(obj, 'foo.bar.baz.buk')); // undefined
If the access string is empty, returns the object. Otherwise, keeps going along access path until second last accessor. If that's an ojbect, returns the last object[accessor]
value. Otherwise, returns undefined.
Upvotes: 61
Reputation: 103
Above answers help you access nested objects only, however you might also want to access data in an object/array data type. You can try this recusive method:
const getValue = (obj, key) => {
const keyParts = key.split(".");
return getValueHelper(obj, keyParts);
};
const getValueHelper = (obj, keyParts) => {
if (keyParts.length == 0) return obj;
let key = keyParts.shift();
if (Array.isArray(obj[key])) {
return obj[key].map((x) => getValueHelper(x, [...keyParts])).flat();
}
return getValueHelper(obj[key], [...keyParts]);
};
//Examples
let data1 = {
a: [{ b: { c: [{ d: [{ e: 1 }] }] } }, { b: { c: [{ d: [{ e: 2 }] }] } }],
};
console.log(getValue(data1, "a.b.c.d.e"));
//Output
//[ 1, 2 ]
let data2 = {
a:{b:1},
};
console.log(getValue(data2, "a.b"));
//Output
//1
p.s. Remove .flat()
to get desired output for arrays.
Upvotes: 2
Reputation: 303
function getPropertyByString(object, propString) {
let value = object;
const props = propString.split('.');
for (let index = 0; index < props.length; index += 1) {
if (props[index] === undefined) break;
value = value[props[index]];
}
return value;
};
const object = {
name: 'any_name',
address: {
number: 77,
test: {
name: 'test'
}
}
}
console.log(getPropertyByString(object, 'address.test.name'))
// test
Upvotes: 1
Reputation: 2098
Using ES6:
var arr = { foo : 1, bar: { baz : 2 }, bee : 3 };
var {foo, bar, bar: {baz}, bee} = arr;
Same as:
// var foo = 1;
// var bar = {baz: 2};
// var baz = 2;
// var bee = 3;
Using lodash: https://lodash.com/docs#get
_.get(arr, 'bar.baz'); //returns 2;
_.get(arr, 'bar.baz[5].bazzz'); //returns undefined wont throw error;
_.get(arr, 'bar.baz[5].bazzz', 'defaultvalue'); // Returns defaultValue because result is undefined
Upvotes: 38
Reputation: 181
A recursive way :
function getValue(obj, path) {
if (!path) return obj;
const properties = path.split('.');
return getValue(obj[properties.shift()], properties.join('.'))
}
const myObj = {
foo: {
bar: {
value: 'good'
}
}
}
console.log(getValue(myObj, 'foo.bar.value')); // good
Upvotes: 18
Reputation: 614
A one liner for you:
const mock = {
target: {
"prop1": {
"prop2": {
"prop3": "sad"
}
}
},
path: "prop1.prop2.prop3",
newValue: "happy"
};
mock.path.split(".").reduce(
(acc, curr, i, src) =>
(curr === src[src.length - 1]) ? acc[src[src.length - 1]] = mock.newValue : acc[curr], mock.target);
console.log(mock.target); //? { prop1: { prop2: { prop3: 'happy' } } }
Upvotes: 5
Reputation: 26201
I have recently developed my own Object method to get an object property nested among objects and arrays regardless how deep it is. It utilizes a single line of recursive approach. Check this out.
Object.prototype.getNestedValue = function(...a) {
return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};
var myObj = { foo : 1, bar: { baz : 2 }, bee : 3 },
bazval = myObj.getNestedValue("bar","baz");
document.write(bazval);
Now let's check a deeper nested array object combo data structure
Object.prototype.getNestedValue = function(...a) {
return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};
var myArr = [{fox: [{turn:[857, 432]}]}, {sax: [{pana:[777, 987]}]}, {ton: [{joni:[123, 567]}]}, {piu: [{burn:[666, 37]}]}, {sia: [{foxy:[404, 696]}]}];
document.write(myArr.getNestedValue(3,"piu",0,"burn",1));
I believe being able to pass search parameters dynamically to existing array methods would make actions like searching, filtering or replacing of deeply nested structures much easy.
Upvotes: 2
Reputation: 1213
Theres a function defined on this blog to safely read nested properties from a JS object
It allows you to mine an object for properties... ie.
safeRead(arr, 'foo', 'bar', 'baz');
and if any part of the object chain is null or undefined it returns an empty string....
Upvotes: 0
Reputation: 44215
You can access the functions arguments where you can pass any number of strings. I also recommend using arr as a parameter for better encapsulation:
function getter() {
var current = arguments[0];
for(var i = 1; i < arguments.length; i++) {
if(current[arguments[i]]) {
current = current[arguments[i]];
} else {
return null;
}
}
return current;
}
var arr = { foo : 1, bar: { baz : 2 }, bee : 3 };
var baz = getter(arr, 'bar', 'baz');
Upvotes: 1
Reputation: 4505
How about change the getter function signature as getter('bar', 'baz')
instead
function getter() {
var v = arr;
for(var i=0; i< arguments.length; i++) {
if(!v) return null;
v = v[arguments[i]];
}
return v;
}
ps. didn't test, but you get the idea ;)
Upvotes: 13