1

The following function does an amazing job of retrieving a deep object key value with a dot notated string:

function getPathValue(obj, path) {
  return new Function('_', 'return _.' + path)(obj);
}

For example, it returns the value for a key like "bar" using the following as the path argument:

'data.abc.foo.bar'

However, some API's pack additional stringified JSON into some key values.

I'm looking for an enhancement to the above function that will handle that case.

For example, Stack's own WebSocket service:

wss://qa.sockets.stackexchange.com/

returns strings like this:

{"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"stackoverflow.com\",\"id\":53819390,\"titleEncodedFancy\":\"Should I start with webGL1 or webGL2 when using Three.js\",\"bodySummary\":\"Spent some time learning and understanding the basics of webGL and I'm now diving into Three.js.\\n\\nI noticed that I have the option of using webGL1 or webGL2. Seems as if webGL1 is the default, however ...\",\"tags\":[\"three.js\",\"webgl\"],\"lastActivityDate\":1545064508,\"url\":\"https://stackoverflow.com/questions/53819390/should-i-start-with-webgl1-or-webgl2-when-using-three-js\",\"ownerUrl\":\"https://stackoverflow.com/users/8226111/romanrogers\",\"ownerDisplayName\":\"romanrogers\",\"apiSiteParameter\":\"stackoverflow\"}"}

And I'd like to be able to use the above function to retrieve values with an input string like:

'data.bodySummary'

Perhaps the input string could look something like:

'data/bodySummary'

where the solidus (/slash) represents a JSON.parse().

Note, this needs to be dynamic as I'd like to make it where the end user can select arbitrary keys to return values for in the general case.

Excel Hero
  • 14,253
  • 4
  • 33
  • 40

2 Answers2

2

You could check if the item from where you take a property is a string an perform a parsing. The return the value of the property.

function getValue(object, path) {
    return path
        .split('.')
        .reduce((o, k) => (typeof o === 'string' ? JSON.parse(o) : o)[k], object);
}

var data = {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"stackoverflow.com\",\"id\":53819390,\"titleEncodedFancy\":\"Should I start with webGL1 or webGL2 when using Three.js\",\"bodySummary\":\"Spent some time learning and understanding the basics of webGL and I'm now diving into Three.js.\\n\\nI noticed that I have the option of using webGL1 or webGL2. Seems as if webGL1 is the default, however ...\",\"tags\":[\"three.js\",\"webgl\"],\"lastActivityDate\":1545064508,\"url\":\"https://stackoverflow.com/questions/53819390/should-i-start-with-webgl1-or-webgl2-when-using-three-js\",\"ownerUrl\":\"https://stackoverflow.com/users/8226111/romanrogers\",\"ownerDisplayName\":\"romanrogers\",\"apiSiteParameter\":\"stackoverflow\"}"};

console.log(getValue(data, 'data.bodySummary'));

ES5

function getValue(object, path) {
    return path
        .split('.')
        .reduce(function (o, k) {
            return (typeof o === 'string' ? JSON.parse(o) : o)[k];
        }, object);
}

var data = {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"stackoverflow.com\",\"id\":53819390,\"titleEncodedFancy\":\"Should I start with webGL1 or webGL2 when using Three.js\",\"bodySummary\":\"Spent some time learning and understanding the basics of webGL and I'm now diving into Three.js.\\n\\nI noticed that I have the option of using webGL1 or webGL2. Seems as if webGL1 is the default, however ...\",\"tags\":[\"three.js\",\"webgl\"],\"lastActivityDate\":1545064508,\"url\":\"https://stackoverflow.com/questions/53819390/should-i-start-with-webgl1-or-webgl2-when-using-three-js\",\"ownerUrl\":\"https://stackoverflow.com/users/8226111/romanrogers\",\"ownerDisplayName\":\"romanrogers\",\"apiSiteParameter\":\"stackoverflow\"}"};

console.log(getValue(data, 'data.bodySummary'));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • I like this a lot. But my requirement is IE11 and ES5. Can you rework that without the arrow function? – Excel Hero Dec 17 '18 at 20:38
  • Thank you. Works perfectly. – Excel Hero Dec 17 '18 at 22:28
  • Is there a way to enhance this so that it works when the JSON has arrays in it and the path includes one or more? Let me know and I can start a new question if you like. – Excel Hero Dec 18 '18 at 20:56
  • it would be better to ask a new question with an example of the data, you have and a mored detailed view, what you want, as it is possible to discuss in the comment section. – Nina Scholz Dec 18 '18 at 21:02
  • I just posted a new question: https://stackoverflow.com/q/53841772/3566998 – Excel Hero Dec 18 '18 at 22:05
0

Here's a function that takes an object or a JSON string as it's first param, a key path string or array as it's second param, and recursively walks the object with the key path.

let object = {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"stackoverflow.com\",\"id\":53819390,\"titleEncodedFancy\":\"Should I start with webGL1 or webGL2 when using Three.js\",\"bodySummary\":\"Spent some time learning and understanding the basics of webGL and I'm now diving into Three.js.\\n\\nI noticed that I have the option of using webGL1 or webGL2. Seems as if webGL1 is the default, however ...\",\"tags\":[\"three.js\",\"webgl\"],\"lastActivityDate\":1545064508,\"url\":\"https://stackoverflow.com/questions/53819390/should-i-start-with-webgl1-or-webgl2-when-using-three-js\",\"ownerUrl\":\"https://stackoverflow.com/users/8226111/romanrogers\",\"ownerDisplayName\":\"romanrogers\",\"apiSiteParameter\":\"stackoverflow\"}"};

function asObject(str) {
    try {
        return JSON.parse(str);
    } catch (e) {
        return str;
    }
}


function valueAtKeyPath(o, path) {
    o = asObject(o);
    if (typeof path === 'string') path = path.split('.');
    
    if (!o || !path.length) return o;
    return valueAtKeyPath(o[path[0]], path.slice(1));
}

console.log(valueAtKeyPath(object, 'action'))
console.log(valueAtKeyPath(object, 'data.id'))
danh
  • 62,181
  • 10
  • 95
  • 136
  • Nice, but it doesn't return string values for paths like: valueAtKeyPath(object, 'action') – Excel Hero Dec 17 '18 at 20:43
  • @ExcelHero - I see what you mean. Fixed. – danh Dec 17 '18 at 20:57
  • Thank you for this. It works. I appreciate you updating it as well. I'm selecting Nina's because it was first and I like how conicse it is. I also like that it returns the string instead of the object for the path: 'data' – Excel Hero Dec 17 '18 at 22:26