How to access a nested property of an object using a string? - javascript

I have the following string:
const str = "prop1.prop2.prop3"
I want to use this string to access the property prop3 of the following object:
const obj = {
prop1: {
prop2:{
prop3:{
// ---- destination point
}
}
}
}
But I'm not able to figure out how to do it?
there must be something that keeps adding the obj[currentProp] so on and so on. and.. isn't there a quicker method? I'm afraid I'm wasting my time on something that can be achieved more easily

This would be my approach:
const access = (path, object) => {
return path.split('.').reduce((o, i) => o[i], object)
}
const obj = {
prop1: {
prop2: {
prop3: {
value: 'foo'
}
}
}
}
const str = 'prop1.prop2.prop3'
console.log(access(str, obj)) // {"value": "foo"}

You can combine split with forEach as follows:
const str = "prop1.prop2.prop3"
const obj = {
prop1: {
prop2:{
prop3:{
a: "b",
c: "d"
}
}
}
}
var srch = obj;
str.split(".").forEach(item => (srch = srch[item]));
console.log(srch); // { a: "b", c: "d"}
console.log(obj);
split converts str's value into an array, which is then looped and on each iteration, srch gets one level deeper.

different ways to access a nested property of an object
using a function accessDeepProp with two arguments the object and path of the nested property!
Recursive way:
function accessDeepProp(obj, path) {
if (!path) return obj;
const properties = path.split(".");
return accessDeepProp(obj[properties.shift()], properties.join("."));
}
For-loop way:
function accessDeepProp(obj, path) {
const properties = path.split(".");
for (let i = 0; i < properties.length; i++) {
if (!obj) return null;
obj = obj[properties[i]];
}
return obj;
}
Eval way: never_use_eval!
function accessDeepProp(objName, path) {
try {
return eval(`${objName}.${path}`);
} catch (e) {
return null;
}
}
you could also use lodash get method

This is the shortest solution, and it supports arrays and ['bracket notation']. Just don't run it against malicious user input.
Update: a better(?) version without eval.
const obj = {
prop1: {
prop2: {
prop3: {
value: 'foo'
}
}
}
}
const str = 'prop1.prop2.prop3'
//console.log(eval("obj." + str))
// a code without eval
var value = (Function("return obj." + str))();
console.log(value);

Related

Get the key of a JS Object from the value

I am trying to get the key of a JS Object in Typescript from an input value, the problem is that the values are inside an Array.
This is what I have seen in the case that the value is not in an array:
const exampleObject = {
key1: 'Geeks',
key2: 'Javascript'
};
function getKeyByValue(object, value) {
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
if (object[prop] === value)
return prop;
}
}
}
console.log(getKeyByValue(exampleObject,'Geeks')) // key1
This is an example of my object that I want to get the key from:
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(object, value) {
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
if (object[prop] === value)
return prop;
}
}
}
console.log(getKeyByValue(exampleObject,'Geeks')) // undefined
I need to get the key without using Array.prototype.includes(), because typing the resulting variable as String gives me an error that it may be undefined.
My problem: I don't know how to go through the array inside the function to find the value and get the key
Update:
What I want is to avoid the possibility of returning an undefined, since the input value will always be inside the object, how can I achieve this?
#segmet
You can use my code
const exampleObject = {
key1: ['Geeks', 'test1', 'test2'],
key2: ['Javascript', 'test3', 'test4']
};
function getKeyByValue(object, value) {
var output = "";
for (var prop in object) {
// finding and removing element from array
object[prop].find(i => {
if (i === value) {
output = prop;
return prop;
}
}
)
}
return output
}
console.log(getKeyByValue(exampleObject, 'Geeks'))
You can achieve this with a single line of code by using 3 JavaScript methods - Object.keys(), Array.find() & Array.indexOf() :
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(object, value) {
const res = Object.keys(exampleObject).find(key => exampleObject[key].indexOf(value) !== -1);
return res || ''
}
console.log(getKeyByValue(exampleObject,'Geeks'))
function getKeyByValue(object, value) {
const key = Object.entries(object).filter(([key, val]) => val.includes(value));
return Object.fromEntries(key);
}
Try this function instead
You will get the object matching your keys
You can do something like this and then check for null
const getKeyFromValue = (obj:Object, value:string | string[]) => {
for (let key in obj) {
if (typeof value === 'string') {
if (obj[ key ].includes(value)) {
return key;
}
}
else if (Array.isArray(value)) {
if (obj[ key ].every(val => value.includes(val))) {
return key;
}
}
}
return null;
}
One more try:
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(obj, data) {
for (const [key, value] of Object.entries(obj)) {
return (~value.indexOf(data)) ? key : false
}
}
console.log(getKeyByValue(exampleObject,'Geeks'))

How do I dynamically get the value of a nested object property in JavaScript?

Say I have the following object:
let exampleObj = {
hello: {
there: {
friend: 'my friend!',
neighbor: 'neighbor!',
world: 'world!'
}
}
}
Is there an efficient way I can create a function such as
function getPropValues(obj, ...keys) {
// ...
}
where if I call getPropValues with the following arguments
const result = getPropValues(exampleObj, 'hello', 'there', 'world');
It would give me the result of exampleObj['hello']['there']['world']?
(i.e. I would expect result to be 'world!' in this case)
you can do this
function getPropValues(obj, ...keys) {
return keys.reduce((p, c) => {
return p[c]
}, obj)
}
Use Array.prototype.reduce() with Optional chaining ?. as a fallback to undefined for missing properties:
const exampleObj = {
hello: {
there: {
friend: 'my friend!',
neighbor: 'neighbor!',
world: 'world!'
}
}
};
const getVal = (ob, ...k) => k.reduce((o, k) => o?.[k], ob);
console.log( getVal(exampleObj, "hello", "there", "world") ); // "world!"
console.log( getVal(exampleObj, "hello", "stranger") ); // undefined
Or if you like a dotted string notation:
(Be careful since bug-prone. Properties can also have dots)
const exampleObj = {
hello: {
there: {
friend: 'my friend!',
neighbor: 'neighbor!',
world: 'world!'
}
}
};
const getVal = (ob, s) => s.split('.').reduce((o, k) => o?.[k], ob);
console.log( getVal(exampleObj, "hello.there.world") ); // "world!"
console.log( getVal(exampleObj, "hello.stranger") ); // undefined
You can easily achieve this using recursion.
let exampleObj = {
hello: {
there: {
friend: "my friend!",
neighbor: "neighbor!",
world: "world!",
},
},
};
function getPropValuesWithIndex(obj, keys, index) {
if (index === keys.length - 1) return obj[keys[index]];
return getPropValuesWithIndex(obj[keys[index]], keys, index + 1);
}
function getPropValues(obj, ...keys) {
return getPropValuesWithIndex(obj, keys, 0);
}
const result1 = getPropValues(exampleObj, "hello", "there", "world");
console.log(result1);
const result2 = getPropValues(exampleObj, "hello", "there", "friend");
console.log(result2);
Here's a much simpler recursive version in only one function, without using index.
let exampleObj = {
hello: {
there: {
friend: 'my friend!',
neighbor: 'neighbor!',
world: 'world!',
},
},
};
function getPropValues(obj, ...keys) {
let currentObj;
let key = keys.shift();
if (keys.length && typeof obj[key] === 'object') {
currentObj = obj[key];
return getPropValues(currentObj, ...keys);
} else return obj[key];
}
const result = getPropValues(exampleObj, 'hello', 'there', 'world');
console.log(result);

Adding dynamic properties to object only if the name is defined

i have a function like this:
const getKeysAs = (key1, key2) => {
return {
[key1]: state.key1,
[key2]: state.key2
}
}
So if state.key1 is 'a' and state.key2 is 'b', calling getKyesAs('one', 'two') would return
{
one: 'a',
two: 'b'
}
Now, if one of the argument is undefined, is there a way to not include it in the returned object ?
You can Conditionally add properties to an Object with object destructuring
const obj = {
...(key1 && { [key1]: state[key1] }),
...(key2 && { [key2]: state[key2] })
};
If some of the args function is undefined, null or 0 (falsy values) then it will no be added to the object.
There is a very scalable way to do it:
const state= {
a: "hello",
}
function getKeysAs (keys) {
return [...arguments].reduce((acc, cur)=> {
const newValue = state[cur] && {[cur]: state[cur]}
return {...acc, ...newValue}
}, {})
}
console.log(getKeysAs("a", "b"))
This way, you can pass as much keys as you need without worrying about scalability & undefined values.
Use Object.assign().
const getKeysAs = (key1, key2) => {
return Object.assign({}, key1 && {[key1]: state[key1]}, key2 && {[key2]: state[key2]});
}
Assuming you actually mean to do state[key1], not state.key1, here is a solution that doesn't create superfluous objects:
const getKeysAs = (...keys) => {
const result = {};
for (const key of keys) {
if (key != null) {
result[key] = state[key];
}
}
return result;
}

How to access key from nested object in javascript

Can you please help me how can I access "name" key from obj3. Please find below example.
I am looking for good approch, I dont want to do :
obj.obj1.obj2.obj3.name
var obj = {
obj1 : {
obj2: {
obj3: {
name: 'jhon'
}
}
}
}
Thanks!
You could theoretically destructure using es6 for example
const {obj1: {obj2: { obj3: {name: b}}}} = obj
console.log(b) //jhon
You can use a recursive function that returns the first non object element.
Obviously this functions works only for structures where the nested objects contains only one object or one value.
var obj = {
obj1 : {
obj2: {
obj3: {
name: 'jhon'
}
}
}
}
const getName = (obj) => {
if (typeof obj[Object.keys(obj)] === 'object') {
return getName(obj[Object.keys(obj)])
} else {
return obj[Object.keys(obj)]
}
}
getName(obj)

How to filter an object with its values in ES6

What is the best way to filter an object this way in ES6?
Starting data:
const acceptedValues = ["value1","value3"]
const myObject = {
prop1:"value1",
prop2:"value2",
prop3:"value3"
}
Expected output:
filteredObject = {
prop1:"value1",
prop3:"value3"
}
You can use reduce() to create new object and includes() to check if value of object exists in array.
const acceptedValues = ["value1", "value3"]
const myObject = {
prop1: "value1",
prop2: "value2",
prop3: "value3"
}
var filteredObject = Object.keys(myObject).reduce(function(r, e) {
if (acceptedValues.includes(myObject[e])) r[e] = myObject[e]
return r;
}, {})
console.log(filteredObject)
For ES6 and if
your task can be solved with only filtering based on keys and
you need static code (you know exactly, what properties you need to filter) and
it does not depend on the app state,
than you can use the following destructuring technique:
const myObject = {
prop1: 'value1',
prop2: 'value2',
prop3: 'value3'
}
const { prop2, ...filteredObject } = myObject
console.info({ filteredObject, prop2 })
And you will have:
filteredObject: {prop1: "value1", prop3: "value3"}
prop2: "value2"
Just to build on top of #Nenad Vracar good answer you could use an object instead of the Array with includes for faster lookup:
const acceptedValues = ["value1", "value3"];
const myObject = {
prop1: "value1",
prop2: "value2",
prop3: "value3"
};
const lookup = acceptedValues.reduce( (memo, prop) => {
memo[prop] = true;
return memo;
});
const filteredObject = Object.keys(myObject).reduce((filtered, key) => {
if(lookup[myObject[key]]){
filtered[key] = myObject[key];
}
return filtered;
}, {});
console.log(filteredObject);
Nor that the includes doesn't do the job, but I thought to provide an alternative view.
Why not a simple for loop?
const acceptedValues = ["value1","value3"]
const myObject = {
prop1:"value1",
prop2:"value2",
prop3:"value3"
};
var filteredObject = {};
for(e in myObject) {
if (myObject.hasOwnProperty(e)) {
if (acceptedValues.indexOf(myObject[e]) != -1) {
filteredObject[e] = myObject[e];
}
}
}
console.log(filteredObject);
Since I haven't seen an answer using Object.entries here's one. Note, due to Object.entries() implementation being significantly slower than Object.keys(), this will also be slower than the accepted answer, but some may prefer this for readability or extendability (easier to pass a different filtering function).
const acceptedValues = ["value1", "value3"];
const myObject = {
prop1:"value1",
prop2:"value2",
prop3:"value3"
};
const filteredEntries = Object.entries(myObject).filter(([, v]) => acceptedValues.includes(v));
const filteredObject = Object.fromEntries(filteredEntries);
Or as a longish one-liner:
const filteredObject = Object.fromEntries(Object.entries(myObject).filter(([, v]) => accepted.includes(v)));
Using a simple for loop and get object by key.
const acceptedValues = ["value1","value3"]
const myObject = {
prop1:"value1",
prop2:"value2",
prop3:"value3"
}
Object.prototype.getKeyByValue = function( value ) {
for( var prop in this ) {
if( this.hasOwnProperty( prop ) ) {
if( this[ prop ] === value )
return prop;
}
}
}
for (var i in acceptedValues) {
if (myObject.getKeyByValue(acceptedValues[i])){
console.log(acceptedValues[i]);
}
}
IMO, the "best way" is the Lodash way
const filtered = _.pick(myObject, acceptedValues)
https://lodash.com/docs/4.17.10#pick
function filter(myObject){
var obj=Object.assign({},myObject);
Object.keys(obj).forEach(function(key) {
if(acceptedValues.indexOf(obj[key])<0) delete obj[key];
});
return obj;
}
const filteredObject=filter(myObject);

Categories

Resources