Javascript: Modifying nested object in ES6 shorthand - javascript

Consider a function returns an nested object and I want to modify the property inside the nested object.
In the below example, I'm calling the function many times or I need to store it in a temporary variable. Is there a way to invoke only once inside the braces and spread/modify inside the same object many times.
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
var modifiedD = {
...getObject(),
a: {
b: {
...getObject().a.b,
d: 4
}
}
}
console.log(modifiedD);

when declaring a key after ...getObject() it replace the whole value. It does not merge the inner object behind a.
So you could do it as you have done and call getObject() multiple time.
An other solution could be to handle it using a function of your own merging the objects, like :
function mergeObjects(obj1, obj2) {
// We are going to copy the value of each obj2 key into obj1
Object.keys(obj2).forEach((x) => {
// If we have an object, we go deeper
if (typeof obj2[x] === 'object') {
if (obj1[x] === void 0) {
obj1[x] = {};
}
mergeObjects(obj1[x], obj2[x]);
} else {
obj1[x] = obj2[x];
}
});
return obj1;
}
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
const modifiedD = mergeObjects(getObject(), {
a: {
b: {
d: 4,
},
},
});
console.log(modifiedD);
WARNING, the function I have made mutate the object which may not be the best answer
Or call it only once and then set the keys one by one like :
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
const modifiedD = getObject();
modifiedD.a.b.d = 4;
console.log(modifiedD);

Further to my previous answer, as Grégory NEUT pointed out you could have a lot larger complexity.
If so, you could simply create two objects and then merge them. I found a function code snippet to be able to do that using Object.assign
Example:
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
var modifiedD = getObject();
var newD = {
a: {
b: {
d: 4
},
y: 1
},
z: 20
}
/** TAKEN FROM https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6 **/
// Merge a `source` object to a `target` recursively
const merge = (target, source) => {
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source)) {
if (source[key] instanceof Object) Object.assign(source[key], merge(target[key], source[key]))
}
// Join `target` and modified `source`
Object.assign(target || {}, source)
return target
}
modifiedD = merge(modifiedD, newD);
console.log(modifiedD);

You can try the following:
getParentObj(path, obj) {
return path.split('.').reduce((o,i)=>o[i], obj);
}
const parent = getParentObj('a.b', getObject());
parent[d] = 24;

Related

How to copy an object in javascript?

I am trying to change only the value of copied object, not the main but both objects change as of the result of running this code.
const Randomdata = {
a: 10,
b: 5,
c: {
f: "value1",
q: "value2"
}
};
function copy(MainObj) {
let ObjCopy = {};
for (let key in MainObj) {
ObjCopy[key] = MainObj[key];
}
return ObjCopy;
}
const newObj = copy(Randomdata);
Randomdata.c.q = "value3";
console.log(newObj);
console.log(Randomdata);
Though JSON.parse and JSON.stringify will work , but you will need a recusive function if want to achieve the same result using for..in. The reason is value of key c is a object and when you are copying it, it is just referencing to the old copy
const Randomdata = {
a: 10,
b: 5,
c: {
f: "value1",
q: "value2"
}
};
function copy(MainObj, ObjCopy) {
for (let key in MainObj) {
// check if the value is a object , if so then
// reclusively call the same function
if (typeof MainObj[key] === 'object') {
copy(MainObj[key], ObjCopy)
} else {
ObjCopy[key] = MainObj[key];
}
}
return ObjCopy;
}
const newObj = copy(Randomdata, {});
Randomdata.c.q = "value3";
console.log(newObj);
console.log(Randomdata);

If function parameter is empty is there a way to pass ...rest of values to that parameter?

If function parameter is empty is there a way to pass ...rest of the destructured values to that parameter with spread operator?
For an example:
const obj = {
/* param: {
a: 2,
b: 3
}, */
c: 1,
d: 3
}
const fun = ({ param = ...rest}) => {
console.log(param);
};
fun(obj);
In this case param is "undefined" and i would like to get the rest of the obj assign to the param {c:1, d:3}
In case when param is defined, I would like to have param data {a:2, b:3}
Use a ternary operator in the function parameter section to determine which properties to log:
const obj = {
/*param: {
a: 2,
b: 3
},*/
c: 1,
d: 3
}
const fun = (args = obj.param ? obj.param : obj) => {
console.log(args)
}
fun(obj)

Get all the keys of objects within another object with property value that matches the query

I have a nested object. I need to filter them out by property of the child object but only get the keys.
I have tried so far to first, inject a property id into each child object and assign the object's key as its value. Then proceed to filter the object, compare property if it will match with the query, then return the injected property id.
let test_obj = {
A: {
a: 1,
b: 1,
},
B: {
a: 1,
b: 2,
},
C: {
a: 1,
b: 3,
}
}
let identify = (e) => {
for (e of Object.entries(e)){
key = e[0];
val = e[1];
val.id = key;
console.log(e);
}
}
identify(test_obj);
let query = (test_obj,prop,val) => (Object.values(test_obj).filter(o => o[prop] == val).map(o=>o.id));
let result = query(test_obj,"b",2);
console.log(result)
It currently return my desired results, yet I feel like I cheated. Is there a way to do this without having to inject another property to determine the key? I feel like I'm missing something, but I can't wrap my head around this.
Instead of adding an additional key, and then filtering values, you can filter the keys like this instead:
const test_obj = {
A: {
a: 1,
b: 1,
},
B: {
a: 1,
b: 2,
},
C: {
a: 1,
b: 3,
}
}
const query = (obj, prop, val) => Object.keys(obj).filter(k => obj[k][prop] === val);
console.log(query(test_obj, "b", 2))
A more elegant solution is to use the reduce functionality, which you can (and always should) use if you find yourself using filter and map:
function findKeysForValue(test_obj, value) {
return Object.entries(test_obj).reduce((myKeys, [objKey, outerValue]) => {
if (Object.values(outerValue).find(nestedValue => nestedValue === value)) {
return [...myKeys, objKey];
}
return myKeys;
}, []);
}

Referencing self from anonymous function

Not sure how to word this, I basically want to extend my class properties so that users can override them. However I'd prefer the anonymous function which extends the properties to just self-execute.
var extend = function(target, sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
extend(target[key], source[key]); // <-- Line 9
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return extend(target, sources);
}.call(this, this, [ defaults, options ]);
This appears to work up until line 9 (see comment above in code). It can't seem to reference itself. However this seems to work fine if this is a named function rather than anonymous.
How can I get this to work?
Why you need it to be anonymous? Debugging gets worse. You can still use a function expression.
const myfunc = function nameFunctionExpression(i) {
if(i === 10) {
return i;
}
nameFunctionExpression(i + 1);
};
myfunc(0);
I am not sure about what you are trying to accomplish. Provide a way to override properties? Maybe post an usage example?
function extend(obj, rest) {
return Object.assign(obj, rest);
}
const obj = { a: 1, nested: { c: 3 } };
// { a: 2, nested: { c: 4 }, b: 3 }
console.log(extend(obj, { a: 2, b: 3, nested: { c: 4 } }));

Javascript generic method to parse object of different parametrs

var data = {
'id': 'object1',
'sceneCapability': {
'updatedAt': '2017-06-19T20:52:45.688Z'
'currentScene': {
'value': {
'number': 1,
'name': '1'
}
},
'outOfTune': {
'value': false
}
},
'lightingCapability': {
'intensity': {
'value': 0
}
},
'tiltCapability': {
'command': {
'value': 'NO'
},
'position': {
'value': 0
}
}
// like this I have different types of more than 20 Capabilities
};
How can I write a generic method to parse this Object? I need to get currentScene value, outOfTune, intensity, command, position, etc...
Sometimes I get only one capability and sometime I get more than 20 capabilities.
I want to avoid doing something like this because in future there might be hundreds of different capabilities
if (obj.lightingCapability && obj.lightingCapability.intensity) {
console.log(obj.lightingCapability.intensity.value)
}
if (device.sceneCapability && device.sceneCapability.outOfTune) {
// do something
}
Output I want something like
currentScene:1,
outOfTune: false,
intensity: 0,
command: 'NO',
position: 0
Maybe something like this will work for you?
A helper function that finds the property you need and returns null if anything along the chain doesn't exist. I added two 'different' versions in case you don't like the array of property names.
var object = {
a: {
b: {
c: {
d: 10
}
}
}
};
function getValue(object, propertyPath) {
var o = object;
var pLen = propertyPath.length;
for (var i = 0; i < pLen; i++) {
var propertyName = propertyPath[i];
if (!o.hasOwnProperty(propertyName))
return undefined;
o = o[propertyName];
}
return o;
}
function getValueFromString(object, path) {
return this.getValue(object, path.split('.'));
}
console.log(getValue(object, ['a', 'b', 'c', 'd'])); //logs 10
console.log(getValueFromString(object, 'a.b.c.d')); //logs 10
console.log(getValue(object, ['a', 'b', 'c', 'e'])); //logs undefined
Based on the discussion we had in the comments of my first answer I realized you meant something different. This should do the trick:
var object = {
a: {
b: {
c: {
value: 10
},
d: {
e: {
value: 20
}
}
}
}
};
function logAllValues(object) {
for (var p in object) {
var o = object[p];
if (o.value)
console.log(p + ': ' + o.value);
else
logAllValues(o);
}
}
logAllValues(object); //logs c:10 and e:20
A slightly hacky way to do this would be to create a helper function that allows the key chain to be passed in as a string and loop over it. For example
function getValue(obj, keyChain){
var keys = keyChain.split('.');
do {
var key = keys.shift();
if (!obj.hasOwnProperty(key)){
return undefined;
}
obj = obj[key];
} while (keys.length > 0);
return obj;
}
getValue(data, "lightingCapability.intensity.value")
I think you just need to install lodash#get
npm i --save lodash.get
var get = require('lodash.get');
if(get('foo.baz.foobaz')) {
alert('yep');
}
but you always will need to know all the paths you need in advance.
Re-implementing this well community tested method will end up in re-inventing the wheel, so, just install and use it.
you can implement some thing like this using ES6 try and catch block
var object = {
a: {
b: {
c: {
value: 10
},
d: {
e: {
value: 20
}
}
}
}
};
function getValue(jsObject) {
try {
return jsObject();
} catch (e) {
return undefined;
}
}
// use it like this
getValue(() => object.a.b); // returns Object {c: Object, d: Object}
getValue(() => object.a.b.c); // returns Object {value: 10}
getValue(() => object.a.b.x); // returns undefined

Categories