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 } }));
Related
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;
This question already has answers here:
Access Javascript nested objects safely
(14 answers)
Closed 4 years ago.
I'm looking for some good strategies for avoiding errors in JavaScript when using dot notation to call the children of children in objects that may or may not exist.
At the bottom of the code snippet below is an example of a solution that works, but is inelegant (at best).
It would be great to see some native JavaScript solutions or even external libraries that can help avoid this kind of error.
const object1 = {
foo: {
bar: {
baz: 'payload'
}
}
};
const object2 = {};
const array = [object1, object2];
// this will fail on object2 because obj.foo is undefined
array.forEach(obj => {
if (obj.foo.bar.baz) {
console.log(obj.foo.bar.baz);
} else {
console.log('undefined');
}
}
);
// this will work, but it's horrible to write all those nested if statements.
array.forEach(obj => {
if (obj) {
if (obj.foo) {
if (obj.foo.bar) {
if (obj.foo.bar.baz) {
console.log(obj.foo.bar.baz);
}
}
}
} else {
console.log('undefinded');
}
}
);
Lodash already did it for us: https://lodash.com/docs#get
const object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
_.get(object, ['a', '0', 'b', 'c']);
// => 3
_.get(object, 'a.b.c', 'default');
// => 'default'
No sure if that's enough of an improvement but you can use a single if statement with the following condition:
(obj && obj.foo && obj.foo.bar && obj.foo.bar.baz)
This will check if obj.foo.bar.baz exists.
const array=[{foo:{bar:{baz:'payload'}}},{}]
array.forEach(obj => {
if (obj && obj.foo && obj.foo.bar && obj.foo.bar.baz) {
console.log(obj.foo.bar.baz);
} else {
console.log('undefined');
}
});
You could chain all checks with logical AND &&.
const
object1 = { foo: { bar: { baz: 'payload' } } },
object2 = {},
array = [object1, object2];
array.forEach(obj => {
if (obj && obj.foo && obj.foo.bar && obj.foo.bar.baz) {
console.log(obj.foo.bar.baz);
} else {
console.log('undefined');
}
});
For an automatic check, you could take an array of keys and return either the value or undefined.
const
getValue = (object, keys) => keys.reduce((o, k) => (o || {})[k], object),
object1 = { foo: { bar: { baz: 'payload' } } },
object2 = {},
array = [object1, object2];
array.forEach(obj => console.log(getValue(obj, ['foo', 'bar', 'baz'])));
Just to share my two cents:
Some time ago I made a function that allowed to safely access deep properties in javascript using proxies:
// Here is where the magic happens
function optional(obj, evalFunc, def) {
// Our proxy handler
const handler = {
// Intercept all property access
get: function(target, prop, receiver) {
const res = Reflect.get(...arguments);
// If our response is an object then wrap it in a proxy else just return
return typeof res === "object" ? proxify(res) : res != null ? res : def;
}
};
const proxify = target => {
return new Proxy(target, handler);
};
// Call function with our proxified object
return evalFunc(proxify(obj, handler));
}
const obj = {
items: [{
hello: "Hello"
}]
};
console.log(optional(obj, target => target.items[0].hello, "def")); // => Hello
console.log(optional(obj, target => target.items[0].hell, {
a: 1
})); // => { a: 1 }
Also, I wrote an article on this for further reference.
I see one smart way: using JSON.stringify to recursively traverse objects like this:
function search(obj, str) {
let flag = false;
JSON.stringify(obj, (key, value) => {
if (typeof value === "object") return value;
else {
// doesn't take into account Date cases etc
if (value.indexOf(str) >= 0) {
flag = true;
}
}
})
return flag;
}
P.S: Not sure about tags.
You could use a traditional iterative and recursive approach by using a depth-first search.
function findString(object, string) {
return Object.keys(object).some(k => typeof object[k] === 'object'
? findString(object[k], string)
: object[k].includes(string)
);
}
var object = { a: 'aaa', b: 'bbb', c: { d: 'ddd', e: { f: 'fff', g: 'ggg' } } };
console.log(findString(object, 'ggg'));
console.log(findString(object, 'foo'));
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
how to compare two objects for equality if they have functions? lodash's isEqual works really well until functions are thrown in:
_.isEqual({
a: 1,
b: 2
}, {
b: 2,
a: 1
});
// -> true
_.isEqual({
a: 1,
b: 2,
c: function () {
return 1;
}
}, {
a: 1,
b: 2,
c: function () {
return 1;
}
});
// -> false
This is what I tried:
_.isEqual(o1, o2, function(val1, val2) {
if(_.isFunction(val1) && _.isFunction(val2)) {
return val1.toString() === val2.toString();
}
})
Lodash supports a customizer function which allows you to write your own equality checks. This seems to be a good enough test to see if the functions are character by character the same.
Are you sure you want to compare functions? If you only care about comparing every property that isn't a function, this is easy to do with lodash:
var o1 = { a: 1, b: 2, c: function() { return 1; } },
o2 = { a: 1, b: 2, c: function() { return 1; } };
_.isEqual(o1, o2)
// → false
_.isEqual(_.omit(o1, _.functions(o1)), _.omit(o2, _.functions(o2)));
// → true
The functions() function returns a list of function properties, and using omit(), you can get rid of them.
Try isEqualWith instead:
import { isEqualWith, isFunction } from 'lodash-es'
const o1 = { fn() {} }
const o2 = { fn() {} }
const equal = isEqualWith(o1, o2, (v1, v2) =>
// if `customizer` returns `undefined`, comparisons are handled by the method instead
isFunction(v1) && isFunction(v2) ? `${v1}` === `${v2}` : undefined,
)
console.log({ equal }) // { equal: true }
As the lodash documentation states:
Functions and DOM nodes are not supported.
https://lodash.com/docs#isEqual