js get name of parent key in an object [closed] - javascript

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I have an object and I want one of the values to be the name of the parent key:
{
name: 'Jon',
address: {
city: 'Vienna',
parent: <How can I dynamically set this to 'address'>
}
}

well i cant understand from your question if you handle nested objects with more then 1 level but this code will work for the first level. if you want unlimited levels you need to do it with recursion
const obj = {
name: 'Jon',
address: {
city: 'Vienna'
}
}
for(let i in obj){
if(typeof obj[i] === 'object'){
obj[i].parent = i;
}
}
console.log(obj)

We can achieve this by making a recursive function which recursively updates all the levels inside the object and child objects with the parent property.
Here is a recursive function which will work for n-levels.
var obj = {
name: 'Jon',
address: {
city: 'Vienna',
pincode :{
val : 110,
Area : {
val : "xyz"
}
},
},
designation : {
post : "Manager",
salary : {
value : "100"
}
}
}
function updateParent(obj,parent){
if(parent)
obj.parent = parent;
for(var key in obj){
if(obj[key].constructor.toString().indexOf("Object") >= 0){
updateParent(obj[key],key);
}
}
}
updateParent(obj);
console.log(obj);

The approach I took was to make the object iterable then use a for...of to search for the desired child property and change the value to it's parent object's name:
var data = {
name: 'Jon',
address: {
city: 'Vienna',
parent: '<How can I dynamically set this to \'address\'>'
}
};
data[Symbol.iterator] = function() {
var i = 0;
var keys = Object.keys(this);
return {
next: function() {
var value = keys[i],
done = false;
i++;
if (i > keys.length) {
done = true;
}
return {
value,
done
}
}
}
}
function setChildProperty(parentPropertyName, childPropertyName) {
for (prop of data) {
if (prop == parentPropertyName) {
data[prop][childPropertyName] = prop;
}
}
}
//Before
console.log(data);
setChildProperty("address", "parent")
//After
console.log(data);

Related

How to output nested object keys separated by a dot - JS [duplicate]

This question already has answers here:
Javascript reflection: Get nested objects path
(3 answers)
Get all paths to a specific key in a deeply nested object
(1 answer)
Get nested objects key as joined string
(2 answers)
Closed 2 years ago.
Code:
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj) {
Object.keys(obj).forEach((key) => {
if (typeof obj === "object") {
Object.keys(obj[key]).forEach((innerKey) => {
console.log(`${key}.${innerKey}`);
});
}
});
}
recursiveKeys(obj);
Desired output:
client.id
client.personal.name
address.street
This code works only for a 2 level object, but it won't work for a 3rd-4th level and deeper, is there a clean way to achieve this?
You need to make your recursiveKeys actually recursive. Pass along the partial property string from the parent object on each recursive call.
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj, propStr = '') {
Object.entries(obj).forEach(([key, val]) => {
const nestedPropStr = propStr + (propStr ? '.' : '') + key;
if (typeof val === 'object') recursiveKeys(val, nestedPropStr);
else console.log(nestedPropStr);
});
}
recursiveKeys(obj);
This code works only for a 2 level object, but it won't work for a
3rd-4th level and deeper, is there a clean way to achieve this?
The problem is you should make your recursiveKeys as it is with 3 steps:
Determine the key result named keyRes
Check if the inner content is an object, then recursive it.
Print the keyRes along with getting out of the recursive, important to avoid infinite loop !!!
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj, previousKey = '') {
Object.entries(obj).forEach(([key, values]) => {
let keyRes = previousKey ? `${previousKey}.${key}` : key; // Step 1
if (typeof values === 'object') // Step 2
recursiveKeys(values, keyRes);
else // Step 3
console.log(keyRes);
});
}
recursiveKeys(obj);
The answer is: recursion!
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj) {
const keys = []
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === "object" && obj[key]) {
//vvvvvvvvvvvvvvvvvvvvvvv--- the function calls itself
recursiveKeys(obj[key]).forEach(innerKey => {
keys.push(`${key}.${innerKey}`)
})
}else{
keys.push(key)
}
});
return keys
}
console.log(recursiveKeys(obj));
If you have access to the new Array#flatMap() method, you can use it to make this even more elegant:
const obj = {
client: {
id: 1,
personal: {
name: "Mike"
}
},
address: {
street: "streetname"
}
};
function recursiveKeys(obj) {
return Object.keys(obj).flatMap(key =>
typeof obj[key] === "object" && obj[key]
? recursiveKeys(obj[key]).map(innerKey => `${key}.${innerKey}`)
: key
);
}
console.log(recursiveKeys(obj));

Javascript | Map to object by dot notation [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I currently have a string which is like the following author.id.
Essentially what I want to do is convert this string into an object. The first becoming a relationship and then the last being an attribute. However the list could actually become longer example:
author.sites.id which would then become the following object:
{
author: {
relationships: {
sites: {
attributes: {
id: 1
}
}
}
}
}
There will always be a top level property on the object being relationships as that is the purpose of doing this, mapping it to a JSON API Spec POST request.
So the example of author.id would produce a object with the following properties:
{
relationships: {
author: {
attributes: {
id:
}
}
}
}
So essentially all of the properties that are split by the . should be nested within a parent property of relationships and the last should always be attributes.
I know you could probably used reduce for this and I have created the following function based off other questions on Stack Overflow but it doesn't do the nesting correctly any doesn't have attributes as a property for the final object which is output.
This is the method which I made based off of other questions:
function mapToObject(object, index) {
index.split('.').reduce(function (obj, i) {
return obj[i];
});
}
Another example based off real world is I have an array of strings...
[{ key: 'author.id', value: '1'}, { key: 'image.id', value: '1'}]
I am then going to loop through these and add them as an object property to a object which is predefined.
let mappedRelationships = {};
let relationships = [
{
key: 'author.id',
value: 1
},
{
key: 'primaryImage.id',
value: 12
}
];
relationships.forEach((relationship) => {
mappedRelationships = {
...mappedRelationships,
// This is where I would need to do the logic for the reduce
}
});
The value of the relationshipsMapped var would be the following:
{
author: {
attributes: {
id: 1
}
},
primaryImage: {
attributes: {
id: 12
}
}
}
You can reduce and create an object with the key-value pair by splitting key in relationship array.
let relationships = [
{
key: 'author.id',
value: 1
},
{
key: 'primaryImage.id',
value: 12
}
];
let mappedRelationships = relationships.reduce((acc, {key, value}) => {
let [k, v] = key.split('.');
acc[k] = {attributes: {[v]: value}};
return acc
}, {});
console.log(mappedRelationships)
You could use condition if string has 3 params
let relationships = [
{
key: 'author.sites.id',
value: 1
},
{
key: 'primaryImage.id',
value: 12
}
];
let mappedRelationships = relationships.reduce((acc, { key, value }) => {
let arr = key.split('.');
let extra = arr.length > 2 ? arr.shift() : null;
let [k1, k2] = arr;
let relationships = { [k1]: { attributes: { [k2]: value } } };
return extra ? {...acc, [extra]:{relationships}} : {...acc, relationships};
}, {});
console.log(JSON.stringify(mappedRelationships))
You could
create an array of keys with the wanted and dynamic parts,
take the last key,
reduce the keys and assign an object if the property has a falsy value,
finally assign the value by taking the last key.
function create(object, { key, value }) {
const parts = ['relationship', 'attributes'],
keys = key.split('.').flatMap((k, i) => [parts[i], k]),
last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
return object;
}
var data = [{ key: 'author.id', value: '1' }, { key: 'image.id', value: '1' }],
object = data.reduce(create, {});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
UPDATE: Based on question change, Updated to support multipule levels
Using with reduce, split and destructuring.
let relationships = [
{
key: "author.sites.id",
value: 1
},
{
key: "primaryImage.id",
value: 12
}
];
const updated = relationships.reduce((acc, { key, value }) => {
const keys = key.split(".");
let item = { attributes: { [keys.pop()]: value } };
while (keys.length > 0) {
item = {
[keys.pop()]: {...item}
}
};
return {
...acc,
...item
};
}, {});
console.log(updated);

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

Search whole javascript object with children

I currently have this object:
var obj = {
1: {
title: 'test',
children: {
2: {
title: 'test2',
children: {}
},
3: {
title: 'test3',
children: {}
}
}
}
};
The whole idea is I make a function to add an item to this object. As parameter I send the parent.
Now, I was wondering how I would get the right item object. For example if I send parent '2', it would get 2: from the children of 1:. The only way I can think of is a for loop, but I don't know if there's a more efficient way. The children can be extended even more, so a parent has children, those children have children endlessly. That's the whole idea at least.
I think with a few items a for loop is okay, but I think if I have over 50 items it's already slow, and it'll even be slower with more.
This solution use Object.keys() for getting all keys of the given object and an array iteration with short ciruit Array.prototype.some() looks for the key. If found the reference is returned, otherwise the item is checked for an object. If so the object reference is taken for a new search with getReference().
var obj = { 1: { title: 'test', children: { 2: { title: 'test2', children: {} }, 3: { title: 'test3', children: {} } } } };
function getReference(o, p) {
var r;
Object.keys(o).some(function (k) {
if (k === p) {
r = o[k];
return true;
}
if (typeof o[k] === 'object') {
r = getReference(o[k], p);
return !!r;
}
});
return r;
}
var x = getReference(obj, '2');
document.write(x.title);
If you want adding to be fast, you can preserve indexes of your child nodes in object or map (ES6). It could look like this:
function Tree() {
this.data = {};
this.indexes = {0: this.data};
}
Tree.prototype = {
addNode: function(parentIndex, index, node) {
// handle cases when parentIndex does not exist
// handle cases when index already exists
this.indexes[index] = node;
var parent = this.indexes[parentIndex];
parent.children = parent.children || {};
parent.children[index] = node;
}
}
var tree = new Tree();
tree.addNode(0, 1, { title: 'test' });
tree.addNode(1, 2, { title: 'test2' });
tree.addNode(1, 3, { title: 'test3' });
console.log(tree.data);

Accessing Complex Javascript object dynamically [duplicate]

This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 7 years ago.
I have a javascript object, something like this :
var obj = { simpleName: "some name" name: { firstName: "anudeep", lastName : "rentala" }, address: { country: "XZ", state:"DF" } }
I also have another object like this :
var foo = { loc = "name.firstName" }
Depending on the foo.loc value, I'd have to access the value in obj object.
In this scenario, I'd need to access obj.name.firstname.
So, far, I've tried something like this:
var props = foo.loc.split(".");
for(var prop in props)
{
if (obj.hasOwnProperty(prop))
{
alert(obj[prop])
}
}
My problem is, I can now access only the name property of obj object, how do I step into it, like name.firstName, I'm aware that obj[name][firstName] would work, but how do i do this dynamically ? Like extend this to obj["prop1"]["prop2"]["prop3"] . .. .["propn"]
There are few missing ,, and firstname vs firstName, but if you fix those, then this works great:
var obj = { simpleName: "some name", name: { firstName: "anudeep", lastName : "rentala" }, address: { country: "XZ", state:"DF" } }
var foo = { loc: "name.firstName" }
var props = foo.loc.split(".");
var output = props.reduce( function(prev,prop) {
if (prev.hasOwnProperty(prop)) {
return prev[prop]
} else {
// however you want to handle the error ...
}
}, obj);
alert(output);
You could fix your code like this:
var props = foo.loc.split(".");
var current = obj;
for(var prop in props)
{
if (current.hasOwnProperty(prop))
{
current = current[prop];
alert(current )
}
}
but that probably won't be very useful once you start having more complex "selectors", for example, using arrays ("names[2].firstname").
Here is a function:
var obj = { simpleName: "some name", name: { firstName: "anudeep", lastName : "rentala" }, address: { country: "XZ", state:"DF" } };
var foo = { loc: "name.firstName" };
var checkObj = function(obj, props) {
var temp = obj;
for(var i = 0, len = props.length; i < len; i++) {
if(temp.hasOwnProperty(props[i])) {
temp = temp[props[i]];
}
else return false;
}
return temp;
};
console.log(checkObj(obj, foo.loc.split('.')));

Categories

Resources