How to access key from nested object in javascript - 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)

Related

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

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);

How to merge two Objects and keep the hierarchy between properties? [duplicate]

This question already has answers here:
How to deep merge instead of shallow merge?
(47 answers)
Closed 8 months ago.
I have a question about the two objects above:
const object1 = { issues: { global: 'a great string!' } };
const object2 = { issues: { cooldown: 'oh well, another message!' } };
For the purpose of my code, I would need to merge everything but keep the hierarchy between the properties. Like this:
Object.assign(object1, object2);
console.log(object1);
// expected output: { issues: { global: 'a great string!', cooldown: 'oh well, another message!' } }
Except that the problem is that the Object.assign() works such that it will overwrite the previous property (here global) to keep only the last property (here cooldown). Like this:
Object.assign(object1, object2);
console.log(object1);
// real output: { issues: { cooldown: 'oh well, another message!' } }
My question is then simple: how do we get the expected result? I guess it's not as simple as a simple Object.assign(), but precisely: how? Knowing that obviously I took here an example and that, in reality, impossible to know in advance which properties will arrive...
Thanking you for help 💙
I think a recursive solution will help for any level of nesting:
const obj1 = { issues: { global: 'a great string!' } };
const obj2 = { issues: { cooldown: 'oh well, another message!' } };
const obj3 = { issues: { global: 'a great string!' } , problems : { name : 'cool' , size : {
medium : 'ok',
small : 'ok'
} } };
const obj4 = { issues: { cooldown: 'oh well, another message!' } , problems : { name : 'cool' , size : {
big : 'not ok',
} } };
const merge = (obj1,obj2) => {
const newObj = {};
for(let key in obj1){
if(typeof obj1[key] !== 'object'){
newObj[key] = obj1[key];
}
else
if(key in obj2){
newObj[key] = merge(obj1[key],obj2[key]);
}
else{
newObj[key] = obj1[key];
}
}
for(let key in obj2){
if(!(key in obj1)){
newObj[key] = obj2[key];
}
}
return newObj;
};
console.log(merge(obj1,obj2));
console.log(merge(obj3,obj4));
You check if keys are matching (available in both objects) and based on that add a new key to your new result object.
Note: The above by defaults handle case when the property values are not objects. You can change and add your desired behaviour
i create this function i think it will do the work
const obj1 = { issues: { global: 'a great string!' } };
const obj2 = { issues: { cooldown: 'oh well, another message!' } };
const obj3 = {
issues: { global: 'a great string!' }, problems: {
name: 'cool', size: {
medium: 'ok',
small: 'ok'
}
}
};
const obj4 = {
issues: { cooldown: 'oh well, another message!' }, problems: {
name: 'cool', size: {
big: 'not ok',
}
}
};
const mergeObj = (obj1, obj2) => {
const Obj1Keys = Object.keys(obj1)
const Obj2Keys = Object.keys(obj1)
const allKeys = [...new Set([...Obj1Keys, ...Obj2Keys])]
const newObj = {}
for (let i = 0; i < allKeys.length; i++) {
const key = allKeys[i]
if (typeof obj1[key] === "object" && typeof obj2[key] === "object")
newObj[key] = mergeObj(obj1[key], obj2[key])
}
return Object.assign(obj1, obj2, newObj)
}
console.log(mergeObj(obj3, obj4));

Update fields in nested objects in Typescript / Javascript

In Firestore you can update fields in nested objects by a dot notation (https://firebase.google.com/docs/firestore/manage-data/add-data?authuser=0#update_fields_in_nested_objects). I wonder how to make that work in Typescript / Javascript.
For example the following object:
const user = {
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: true
}
},
token: {
custom: 'safghhattgaggsa',
public: 'fsavvsadgga'
}
}
How can I update this object with the following changes:
details.email.verified = false;
token.custom = 'kka';
I already found that Lodash has a set function:
_.set(user, 'details.email.verified', false);
Disadvantage: I have to do this for every change. Is their already a method to update the object with an object (like firestore did)?
const newUser = ANYFUNCTION(user, {
'details.email.verified': false,
'token.custom' = 'kka'
});
// OUTPUT for newUser would be
{
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: false
}
},
token: {
custom: 'kka',
public: 'fsavvsadgga'
}
}
Does anyone know an good solution for this? I already found more solutions if I only want to change one field (Dynamically set property of nested object), but no solution for more than one field with one method
I think you are stuck with using a function but you could write it yourself. No need for a lib:
function set(obj, path, value) {
let parts = path.split(".");
let last = parts.pop();
let lastObj = parts.reduce((acc, cur) => acc[cur], obj);
lastObj[last] = value;
}
set(user, 'details.email.verified', false);
if what you want to do is merge 2 objects then it is a bit trickier:
function forEach(target, fn) {
const keys = Object.keys(target);
let i = -1;
while (++i < keys.length) {
fn(target[keys[i]], keys[i]);
}
}
function setValues(obj, src) {
forEach(src, (value, key) => {
if (value !== null && typeof (value) === "object") {
setValues(obj[key], value);
} else {
obj[key] = value;
}
});
}
let obj1 = {foo: {bar: 1, boo: {zot: null}}};
let obj2 = {foo: {baz: 3, boo: {zot: 5}}};
setValues(obj1, obj2);
console.log(JSON.stringify(obj1));
One solution in combination with lodash _.set method could be:
function setObject(obj, paths) {
for (const p of Object.keys(paths)) {
obj = _.set(obj, p, paths[p]);
}
return obj;
}

JavaScript object assign

I'm trying to update an deeply nested object without overriding existing properties, but can't find an elegant way to do this, for example:
const data = {
items: {
one: {
active: false,
id: '123'
},
two: {
active: true
}
}
}
const updatedData = {
items: {
one: {
active: true
}
}
}
The end result should be:
{
items: {
one: {
active: true,
id: '123'
},
two: {
active: true
}
}
}
However, using Object.assign or spread operator, will replace items.one with only {active: true} and not retain the id. Is there a way to do this without recursively going through the object?
function merge(source, into) {
for(let key in into){
if(typeof into[key] === "object") {
merge(source[key] || (source[key] = {}), into[key]);
} else {
source[key] = into[key];
}
}
}
A recursive function makes it pretty simple. Iterate the properties of the new data. If both the target and source for a property reference an object, call the function recursively with the two objects. Otherwise just assign the source value to the target value.
const data = {
items: {
one: {
active: false,
id: '123'
},
two: {
active: true
}
}
}
const updatedData = {
items: {
one: {
active: true
}
}
}
updateWith(data, updatedData);
console.log(data);
function updateWith(target, newData) {
for (const [k, v] of Object.entries(newData)) {
if (typeof v === "object" && typeof target[k] === "object") {
updateWith(target[k], v);
} else {
target[k] = v;
}
}
}
Given that you reference an arbitrary data.items[key] object, you can do the following:
data.items[key] = Object.assign({}, data.items[key], updatedData.items[key]);
The above will replace the old object value with a new one, with properties copied from the original and ultimately replacing any properties provided by the updated data.

How to deeply remove keys in object?

I have this json object returned from an API that has a few quirks, and I'd like to normalize it so I can process the input the same for every response. These means getting rid of superfluous keys:
Response:
{
_links: {...},
_embedded: {
foo: [
{
id: 2,
_embedded: {
bar: []
}
}
]
}
}
So I'd like to remove all the _embedded keys and flatten it, like so:
{
_links: {...},
foo: [
{
id: 2,
bar: []
}
]
}
This is what I have at the moment, but it only works for the top level and I don't think it'll play well with arrays.
_.reduce(temp1, function(accumulator, value, key) {
if (key === '_embedded') {
return _.merge(accumulator, value);
}
return accumulator[key] = value;
}, {})
Loop in recursion on all of your keys, once you see a key which start with _
simply remove it.
Code:
var
// The keys we want to remove from the Object
KEYS_TO_REMOVE = ['_embedded'],
// The data which we will use
data = {
_links: {'a': 1},
_embedded: {
foo: [
{
id: 2,
_embedded: {
bar: []
}
},
{
id: 3,
_embedded: {
bar: [
{
id: 4,
_embedded: {
bar: []
}
}
]
}
}
]
}
};
/**
* Flatten the given object and remove the desired keys if needed
* #param obj
*/
function flattenObject(obj, flattenObj) {
var key;
// Check to see if we have flatten obj or not
flattenObj = flattenObj || {};
// Loop over all the object keys and process them
for (key in obj) {
// Check that we are running on the object key
if (obj.hasOwnProperty(key)) {
// Check to see if the current key is in the "black" list or not
if (KEYS_TO_REMOVE.indexOf(key) === -1) {
// Process the inner object without this key
flattenObj[key] = flattenObject(obj[key], flattenObj[key]);
} else {
flattenObject(obj[key], flattenObj);
}
}
}
return flattenObj;
}
console.log(flattenObject(data));
So, basically you already have almost all of the code you need. All we have to do is wrap it in a function so we can use recursion. You'll see we only add a check to see if it is an object, if it is, we already have a function that knows how to flatten that object, so we'll just call it again with the key that we need to flatten.
function flatten(temp1) { // Wrap in a function so we can use recursion
return _.reduce(temp1, function(accumulator, value, key) {
if (key === '_embedded') {
return _.merge(accumulator, value);
} else if (value !== null && typeof value === 'object') // Check if it's another object
return _.merge(accumulator, flatten(value)) // Call our function again
return accumulator[key] = value;
}, {})
}
I'll be able to test it in a bit, but this should be what you need.
Got it!
function unEmbed(data) {
return _.reduce(data, function(accumulator, value, key) {
const returnableValue = _.isObject(value) ? unEmbed(value) : value;
if (key === 'embedded') {
return _.merge(accumulator, returnableValue);
}
accumulator[key] = returnableValue;
return accumulator;
}, {});
}
Problem before I was returning return accumulator[key] = returnableValue, which worked out to be return returnableValue.

Categories

Resources