JavaScript - Get nested property's parent object - javascript

let's say I have a nested object like this:
let object = {
another : {
yet_another : {
last_one : {
some_property : [1,2,3]
}
}
}
}
I can access some_property like this:
object.another.yet_another.last_one.some_property;
And let's say I'm referring to this object in a variable:
var x = object.another.yet_another.last_one.some_property;
How can I tell what's the parent object of some_property if I only have access the x variable? is it even possible in JavaScript?

No, it's not possible. An object doesn't have a "parent" per se. Observe:
let object = {
another : {
yet_another : {
last_one : {
some_property : [1,2,3]
}
}
}
};
let another_object = {
foo: object.another.yet_another.last_one.some_property
};
Now what? The array is now equally a member of both objects.

No, because when doing the following line;
var x = object.another.yet_another.last_one.some_property;
then you assign x to the value of some_property, nothing more.

Based on your comment to an answer, the solution to your (actual) problem should be don't move objects around. Mutability can be very very expensive (eventually prohibitively) when it comes to maintaining an application.
Just create new objects:
const firstObject = {
prop1: 'some value',
prop2: {
prop3: 'some value',
prop4: [1,2,3,4]
}
}
// don't do
const secondObject = { }
secondObject.prop2.prop4 = firstObject.prop2.prop4
// instead do
const secondObject = { ... }
const newObject = {
...secondObject,
prop2: {
...secondObject.prop2,
prop4: firstObject.prop2.prop4
}
}
You may want to look into immutablejs.

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

modifying dictionary keys in Javascript

I am trying to dynamically create an n dim object finalObj looking like this for n=3
children = { 'value1' : 'some1' , 'value2' : 'some2' , 'value3' :'some3' }
item = { 'key1' : 'value1' , 'key2':'value2' , 'key3':'value3' }
basically, the resulting object would look like this.
finalObj = { parent : { 'some1' : { 'some2' : { 'some3':{} } } } }
I am creating an n-depth Object below here.
var parent ={}
var finalObj
function makechildren( children, depth ){
if (depth>0){
makechildren({children}, depth-1);
}
else
{ finalObj=children
console.log('finalObj',finalObj)
}
return finalObj
}
Promise.resolve(makechildren(parent,4))
.then(function(resp){
console.log("resp is",resp);
})
This prints:
{ "children": { "children": { "children": { "children": {} } } } }
Now, how to turn
parent.children.children.children
with
item ={'key1':'value1','key2':'value2','key3':'value3'}
into
parent.children[ item['key1']].children[ item['key2']].children[ item['key3']]
which is essentially
parent.children['value1'].children['value2'].children['value3']....
I have tried making a copy of the original dictionary and altering the keys with a loop and assigning each parent.children[ item['key1']] to the rest of the multidimensional dictionary but didnt go really far.
parent1 = JSON.parse(JSON.stringify(parent))
for (i in Object.keys(parent))
{
parent1[ item['key'+i] ] = parent.children
}
However, iam stuck here on how to complete it this way. Any idea?
Loop through the values in item, using each one to get the corresponding value from children, and use this as the property for a new object. To make the nested object, use a variable to hold the object at the current depth, which you update each time through the loop.
children = { 'value1' : 'some1' , 'value2' : 'some2' , 'value3' :'some3' };
item = { 'key1' : 'value1' , 'key2':'value2' , 'key3':'value3' };
finalObj = { parent: {} };
var cur = finalObj.parent;
Object.values(item).forEach(val => {
cur[children[val]] = {};
cur = cur[children[val]];
});
console.log(JSON.stringify(finalObj));
Note that the order of items read from an object is not guaranteed, so there's no assurance that this will nest things in the desired order. You should use an array if you want order to be maintained.

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)

Is it possible to destructure onto an existing object? (Javascript ES6)

For example if I have two objects:
var foo = {
x: "bar",
y: "baz"
}
and
var oof = {}
and I wanted to transfer the x and y values from foo to oof. Is there a way to do that using the es6 destructuring syntax?
perhaps something like:
oof{x,y} = foo
While ugly and a bit repetitive, you can do
({x: oof.x, y: oof.y} = foo);
which will read the two values of the foo object, and write them to their respective locations on the oof object.
Personally I'd still rather read
oof.x = foo.x;
oof.y = foo.y;
or
['x', 'y'].forEach(prop => oof[prop] = foo[prop]);
though.
IMO this is the easiest way to accomplish what you're looking for:
let { prop1, prop2, prop3 } = someObject;
let data = { prop1, prop2, prop3 };
// data === { prop1: someObject.prop1, ... }
Basically, destructure into variables and then use the initializer shorthand to make a new object. No need for Object.assign
I think this is the most readable way, anyways. You can hereby select the exact props out of someObject that you want. If you have an existing object you just want to merge the props into, do something like this:
let { prop1, prop2, prop3 } = someObject;
let data = Object.assign(otherObject, { prop1, prop2, prop3 });
// Makes a new copy, or...
Object.assign(otherObject, { prop1, prop2, prop3 });
// Merges into otherObject
Another, arguably cleaner, way to write it is:
let { prop1, prop2, prop3 } = someObject;
let newObject = { prop1, prop2, prop3 };
// Merges your selected props into otherObject
Object.assign(otherObject, newObject);
I use this for POST requests a lot where I only need a few pieces of discrete data. But, I agree there should be a one liner for doing this.
EDIT: P.S. -
I recently learned you can use ultra destructuring in the first step to pull nested values out of complex objects! For instance...
let { prop1,
prop2: { somethingDeeper },
prop3: {
nested1: {
nested2
}
} = someObject;
let data = { prop1, somethingDeeper, nested2 };
Plus, you could use spread operator instead of Object.assign when making a new object:
const { prop1, prop2, prop3 } = someObject;
let finalObject = {...otherObject, prop1, prop2, prop3 };
Or...
const { prop1, prop2, prop3 } = someObject;
const intermediateObject = { prop1, prop2, prop3 };
const finalObject = {...otherObject, ...intermediateObject };
No, destructuring does not support member expressions in shorthands but only plain propertynames at the current time. There have been talks about such on esdiscuss, but no proposals will make it into ES6.
You might be able to use Object.assign however - if you don't need all own properties, you still can do
var foo = …,
oof = {};
{
let {x, y} = foo;
Object.assign(oof, {x, y})
}
Other than Object.assign there is the object spread syntax which is a Stage 2 proposal for ECMAScript.
var foo = {
x: "bar",
y: "baz"
}
var oof = { z: "z" }
oof = {...oof, ...foo }
console.log(oof)
/* result
{
"x": "bar",
"y": "baz",
"z": "z"
}
*/
But to use this feature you need to use stage-2 or transform-object-rest-spread plugin for babel. Here is a demo on babel with stage-2
BabelJS plugin
If you are using BabelJS you can now activate my plugin babel-plugin-transform-object-from-destructuring (see npm package for installation and usage).
I had the same issue described in this thread and for me it was very exhausting when you create an object from a destructuring expression, especially when you have to rename, add or remove a property. With this plugin maintaining such scenarios gets much more easier for you.
Object example
let myObject = {
test1: "stringTest1",
test2: "stringTest2",
test3: "stringTest3"
};
let { test1, test3 } = myObject,
myTest = { test1, test3 };
can be written as:
let myTest = { test1, test3 } = myObject;
Array example
let myArray = ["stringTest1", "stringTest2", "stringTest3"];
let [ test1, , test3 ] = myArray,
myTest = [ test1, test3 ];
can be written as:
let myTest = [ test1, , test3 ] = myArray;
It's totally possible. Just not in one statement.
var foo = {
x: "bar",
y: "baz"
};
var oof = {};
({x: oof.x, y: oof.y} = foo); // {x: "bar", y: "baz"}
(Do note the parenthesis around the statement.)
But keep in mind legibility is more important than code-golfing :).
Source: http://exploringjs.com/es6/ch_destructuring.html#sec_assignment-targets
You can just use restructuring for that like this:
const foo = {x:"a", y:"b"};
const {...oof} = foo; // {x:"a", y:"b"}
Or merge both objects if oof has values:
const foo = {x:"a", y:"b"};
let oof = {z:"c"}
oof = Object.assign({}, oof, foo)
You can return the destructured object in an arrow function, and use Object.assign() to assign it to a variable.
const foo = {
x: "bar",
y: "baz"
}
const oof = Object.assign({}, () => ({ x, y } = foo));
You can destruct an object assigning directly to another object attribute.
Working example:
let user = {};
[user.name, user.username] = "Stack Overflow".split(' ');
document.write(`
1st attr: ${user.name} <br />
2nd attr: ${user.username}`);
You can work with destructing using variables with the same name of object attribute you want to catch, this way you don't need to do:
let user = { name: 'Mike' }
let { name: name } = user;
Use this way:
let user = { name: 'Mike' }
let { name } = user;
The same way you can set new values to object structures if they have the same attribute name.
Look this working example:
// The object to be destructed
let options = {
title: "Menu",
width: 100,
height: 200
};
// Destructing
let {width: w, height: h, title} = options;
// Feedback
document.write(title + "<br />"); // Menu
document.write(w + "<br />"); // 100
document.write(h); // 200
Try
var a = {a1:1, a2: 2, a3: 3};
var b = {b1:1, b2: 2, b3: 3};
const newVar = (() => ({a1, a2, b1, b2})).bind({...a, ...b});
const val = newVar();
console.log({...val});
// print: Object { a1: 1, a2: 2, b1: 1, b2: 2 }
or
console.log({...(() => ({a1, a2, b1, b2})).bind({...a, ...b})()});
I came up with this method:
exports.pick = function pick(src, props, dest={}) {
return Object.keys(props).reduce((d,p) => {
if(typeof props[p] === 'string') {
d[props[p]] = src[p];
} else if(props[p]) {
d[p] = src[p];
}
return d;
},dest);
};
Which you can use like this:
let cbEvents = util.pick(this.props.events, {onFocus:1,onBlur:1,onCheck:'onChange'});
let wrapEvents = util.pick(this.props.events, {onMouseEnter:1,onMouseLeave:1});
i.e., you can pick which properties you want out and put them into a new object. Unlike _.pick you can also rename them at the same time.
If you want to copy the props onto an existing object, just set the dest arg.
This is kind of cheating, but you can do something like this...
const originalObject = {
hello: 'nurse',
meaningOfLife: 42,
your: 'mom',
};
const partialObject = (({ hello, your }) => {
return { hello, your };
})(originalObject);
console.log(partialObject); // ​​​​​{ hello: 'nurse', your: 'mom' }​​​​​
In practice, I think you'd rarely want to use that though. The following is MUCH more clear... but not nearly as fun.
const partialObject = {
hello: originalObject.hello,
your: originalObject.your,
};
Another completely different route, which includes mucking with the prototype (careful now...):
if (!Object.prototype.pluck) {
Object.prototype.pluck = function(...props) {
return props.reduce((destObj, prop) => {
destObj[prop] = this[prop];
return destObj;
}, {});
}
}
const originalObject = {
hello: 'nurse',
meaningOfLife: 42,
your: 'mom',
};
const partialObject2 = originalObject.pluck('hello', 'your');
console.log(partialObject2); // { hello: 'nurse', your: 'mom' }
This is the most readable and shortest solution I could come up with:
let props = {
isValidDate: 'yes',
badProp: 'no!',
};
let { isValidDate } = props;
let newProps = { isValidDate };
console.log(newProps);
It will output { isValidDate: 'yes' }
It would be nice to some day be able to say something like let newProps = ({ isValidDate } = props) but unfortunately it is not something ES6 supports.
You can use JSON class methods to achieve it as follows
const foo = {
x: "bar",
y: "baz"
};
const oof = JSON.parse(JSON.stringify(foo, ['x','y']));
// output -> {x: "bar", y: "baz"}
Pass properties that need to be added to the resulting object as second argument to stringify function in an array format.
MDN Doc for JSON.stringify
This works in chrome 53.0.2785.89
let foo = {
x: "bar",
y: "baz"
};
let oof = {x, y} = foo;
console.log(`oof: ${JSON.stringify(oof)}`);
//prints oof: { "x": "bar", "y": "baz"}
It's not a beautiful way, nor I recommend it, but it's possible this way, just for knowledge.
const myObject = {
name: 'foo',
surname: 'bar',
year: 2018
};
const newObject = ['name', 'surname'].reduce(
(prev, curr) => (prev[curr] = myObject[curr], prev),
{},
);
console.log(JSON.stringify(newObject)); // {"name":"foo","surname":"bar"}

Is there any way to use a numeric type as an object key?

It seems that when I use a numeric type as a key name in an object, it always gets converted to a string. Is there anyway to actually get it to store as a numeric? The normal typecasting does not seem to work.
Example:
var userId = 1;
console.log( typeof userId ); // number
myObject[userId] = 'a value';
console.dir(myObject);
Dir Output:
{
'1': 'a value'
}
What I want is this:
{
1: 'a value'
}
Advice?
No, this is not possible. The key will always be converted to a string. See Property Accessor docs
Property names must be strings. This means that non-string objects cannot be used as keys in the object. Any non-string object, including a number, is typecasted into a string via the toString method.
> var foo = {}
undefined
> foo[23213] = 'swag'
'swag'
> foo
{ '23213': 'swag' }
> typeof(Object.keys(foo)[0])
'string'
In an object, no, but I have found Map extremely useful for this application. Here is where I have used it for numeric keys, a key-based event.
onKeydown(e) {
const { toggleSidebar, next, previous } = this.props;
const keyMapping = new Map([
[ 83, toggleSidebar ], // user presses the s button
[ 37, next ], // user presses the right arrow
[ 39, previous ] // user presses the left arrow
]);
if (keyMapping.has(e.which)) {
e.preventDefault();
keyMapping.get(e.which)();
}
}
Appears to be by design in ECMA-262-5:
The Property Identifier type is used to associate a property name with a Property Descriptor. Values of the Property Identifier type are pairs of the form (name, descriptor), where name is a String and descriptor is a Property Descriptor value.
However, I don't see a definite specification for it in ECMA-262-3.
Regardless, I wouldn't attempt to use non-strings as property names.
you can use, Map if you want different datatype as keys
const map1 = new Map();
map1.set(1,3)
map1.set('1','string')
// expected output: 3
console.log(map1.get(1)) //output 3;
console.log(map1.get('1')) //output 'string';
Here is the solution. Please tell me the environmental setups if this is not working
const screens = {
"768": "large",
"200": "small"
}
const keys = Object.keys(screens).map(key => parseInt(key))
// OR Number(key)
console.log(keys) // Output [200, 768]
Do we need something like this?
var userId = 1;var myObject ={};
console.log( typeof userId ); // number
myObject[userId] = 'a value';
console.dir(myObject);
Console:
Object
1
:
"a value"
You can't, but you can always convert keys to a numbers
const data = { 15: "value", name: "Apple" };
const result = Object.keys(data) // get keys as an array
.map((item) => {
return parseInt(item); // convert to integer number
})
.filter((item) => !isNaN(item)); // remove non number elements
console.log(result); //Output: [15]
const a = {
'1': 'a value'
}
//by using a + before any string value it will convert(parse) that into a number
const b = Object.key(a);
console.log(+b); //parse
console.log(typeof b); //number
Per Mozilla:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax[Spread syntax]1
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
const merge = ( ...objects ) => ( { ...objects } );
let mergedObj1 = merge (obj1, obj2);
// Object { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }
let mergedObj2 = merge ({}, obj1, obj2);
// Object { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }
Just order the items before hand and you should get the result you want.
So for your case:
const merge = (...objects) => ({...objects});
//An object with numeric keys
const values = ["a value", "another value", "and another value"];
let merged = merge(...values);
console.log(merged);
You can try this:
arr = {}
function f(a,b,c) {
arr = arguments
}
f("*","#","_")
console.log(arr)
//returns Object { 0: "*", 1: "#", 2: "_" }```
In JavaScript, numerical strings and numbers are interchangeable, so
myObject[1] == myObject['1']
If you really want number to be the key for an object, you might want an array (i.e. created with new Array() or []).

Categories

Resources