Can I use an object as a template for other objects? - javascript

I'm trying to reduce the amount of duplicate code i'm writing in JS objects. I have a methods that I want to use where almost nothing is changing except the target and I'd like to extract that out and somehow get the target through the objects property name. Hopefully the example I put together makes sense.
myObject = {
d: {
get: function(list, id) {
// do stuff
},
prop1: {
data: [],
list: myObject.config.lists.prop1.guid,
get: function(a,b) {
myObject.d.get(a,b)
}
},
// I want to write this once and use the object key ("prop2") as an argument
prop2: {
data: [],
list: myObject.config.lists.prop2.guid,
get: function(a,b) {
myObject.d.get(a,b)
}
}
}
};
Tried something like this but getting error "Cannot read prop 'spec' of undefined:"
myObject = {
d: {
get: function(list, id) {
// do stuff
}
},
// Use this to duplicate shared funtions for similar
spec: function(target) {
return {
data: [],
list: myObject.config.lists[target].guid,
get: function() {
myObject.d.get(a, b);
},
update: "",
delete: ""
};
},
// some how return `myObject.spec.get()`, allowing me to use myObject.d.prop1.get()
prop1: myObject.spec.apply(this, "prop1"),
prop2: myObject.spec.apply(this, "prop2")
};

So far the only way I was able to get it working was by setting prop1 and prop2 outside of the initial deceleration like this and explicitly declaring the target like #Bergi suggested:
var myObject = myObject || {};
myObject = {
d: {
get: function(list, id) {
// do stuff
}
},
// Use this to duplicate shared funtions for similar
spec: function(target) {
return {
data: [],
list: target,
get: function() {
myObject.d.get(a, b);
},
update: "",
delete: ""
};
}
};
// some how return `myObject.spec.get()`, allowing me to use myObject.d.prop1.get()
myObject.prop1 = myObject.spec("prop1");
myObject.prop2 = myObject.spec("prop2");

Related

How to successfully "merge" two sets of plain-objects with getters?

Below I have two sets of plain-objects, and within each there is a getter. I'd love to find a way to merge these two objects. However when I merge them I'd like them to still be getters. I do not want the values within side getters to be resolved.
const love = {
get cats() {
return 'meow';
},
};
const hate = {
get dogs() {
return 'woof';
},
};
console.log({...love, ...hate}); // { cats: 'meow', dogs: 'woof' }
console.log(Object.assign(love, hate)); // { cats: [Getter], dogs: 'woof' }
Use defineProperties to put the properties on the object, spreading into it values from Object.getOwnPropertyDescriptors:
const love = {
get cats() {
return 'meow';
},
};
const hate = {
get dogs() {
return 'woof';
},
};
const result = Object.defineProperties({}, {
...Object.getOwnPropertyDescriptors(love),
...Object.getOwnPropertyDescriptors(hate),
});
console.log(Object.getOwnPropertyDescriptor(result, 'dogs'));
Made this little nugget from #CertainPerformances answer:
function mergeGetters<A, B>(a: A, b: B): A & B {
const result = Object.defineProperties(
{},
{
...Object.getOwnPropertyDescriptors(a),
...Object.getOwnPropertyDescriptors(b),
}
);
return result;
}

How do i use spread syntax to copy the values of only specific keys from json object (returned from API)

In React Redux, Reducers state change needs to copy values of only specific keys. How do i do that using spread syntax.
const initialState = {
Information: {
Manufacturer: undefined,
Model: undefined
}
};
My structure is as above. I get a response from REST as below:
{
"#odata.context": "/redfish/v1/$metadata#ComputerSystem.ComputerSystem",
"#odata.id": "/redfish/v1/Systems/1/",
"Bios":{
"#odata.id": "/redfish/v1/systems/1/bios/"
"Manufacturer": "ABC"
}
How do i extract value of only Manufacturer.
If you have a code example, I could give a better answer, but here's a shot:
function getSpecificKeys (input) {
const { foo, bar, baz, ...other } = input
return { foo, bar, baz } // to return only foo, bar baz
// or, return every other key/value
// return other
}
Update
This is what I understood from your comment.
// You have a data structure like this:
const info = { a: undefined, b: undefined }
// You want to write a function that updates info with payload
// where payload looks like this
const payload = {
model: { a: "a value" },
date: { b: "b value" }
}
// therefore the function should look like this
function mergePayload (info, payload) {
const { model: { a }, date: { b } } = payload
return { ...info, a, b }
// NOTE that this will return a new object and not modify info reference
// if you want to MODIFY info, then do this:
// info.a = a
// info.b = b
}

How to retrieve corresponding object literal of a regular expression instance?

To retrieve the literal object form of an instance of the global String Object in the console we simply do:
var myString = new String("Hello Stackoverflow!");
console.log(myString);
/* console outputs: String {0: "H", 1: "e", 2: "l",..., 18: "w",
19: "!", length: 20, [[PrimitiveValue]]: "Hello Stackoverflow!"} */
But when one creates a regular expression instance of global RegExp object and try to get the object literal form, it won't work and console will just output the regular expression pattern and flags.
var myRegexp = new RegExp("\\d+","g");
console.log(myRegexp);
/* console outputs: /\d+/g while I would expect RegExp{..., global:true,...}
basically the look of an object with curly braces and properties*/
How can I retrieve that regular expression object instance with all its properties and show it in the console?
Actually all the properties of the RexExp are not enumerable, so can't be shown in a really easy way.
Moreover, overriding the toString() method of an object you can change what is going to be printed. For example:
var myRegexp = new RegExp("\\d+","g");
myRegexp.toString = function() {
return 'I am a regex and I dont want to show my properties!';
};
console.log(myRegexp);
Said so, I created a jsfiddle following an MDN post (the link will follow) which will print all the properties you want. I just implemented a sample in the jsfiddle and here, but you need to play a little bit with it in order to get the print as you want and with the correct properties you want
var SimplePropertyRetriever = {
getOwnEnumerables: function(obj) {
return this._getPropertyNames(obj, true, false, this._enumerable);
// Or could use for..in filtered with hasOwnProperty or just this: return Object.keys(obj);
},
getOwnNonenumerables: function(obj) {
return this._getPropertyNames(obj, true, false, this._notEnumerable);
},
getOwnEnumerablesAndNonenumerables: function(obj) {
return this._getPropertyNames(obj, true, false, this._enumerableAndNotEnumerable);
// Or just use: return Object.getOwnPropertyNames(obj);
},
getPrototypeEnumerables: function(obj) {
return this._getPropertyNames(obj, false, true, this._enumerable);
},
getPrototypeNonenumerables: function(obj) {
return this._getPropertyNames(obj, false, true, this._notEnumerable);
},
getPrototypeEnumerablesAndNonenumerables: function(obj) {
return this._getPropertyNames(obj, false, true, this._enumerableAndNotEnumerable);
},
getOwnAndPrototypeEnumerables: function(obj) {
return this._getPropertyNames(obj, true, true, this._enumerable);
// Or could use unfiltered for..in
},
getOwnAndPrototypeNonenumerables: function(obj) {
return this._getPropertyNames(obj, true, true, this._notEnumerable);
},
getOwnAndPrototypeEnumerablesAndNonenumerables: function(obj) {
return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable);
},
// Private static property checker callbacks
_enumerable: function(obj, prop) {
return obj.propertyIsEnumerable(prop);
},
_notEnumerable: function(obj, prop) {
return !obj.propertyIsEnumerable(prop);
},
_enumerableAndNotEnumerable: function(obj, prop) {
return true;
},
// Inspired by http://stackoverflow.com/a/8024294/271577
_getPropertyNames: function getAllPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) {
var props = [];
do {
if (iterateSelfBool) {
Object.getOwnPropertyNames(obj).forEach(function(prop) {
if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) {
props.push(prop);
}
});
}
if (!iteratePrototypeBool) {
break;
}
iterateSelfBool = true;
} while (obj = Object.getPrototypeOf(obj));
return props;
}
};
var myRegexp = new RegExp("\\d+","g");
SimplePropertyRetriever.getPrototypeNonenumerables(myRegexp).forEach(function(el) {
console.log(el + ": " + myRegexp[el]);
});
Here the link:
https://developer.mozilla.org/it/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
Here a jsfiddle:
https://jsfiddle.net/t3bp4tnq/1/
I hope this helps
console.dir prints a tree representation of the object.
console.dir(myRegexp);
Both source and global properties of myRegexp are not enumerable. You can get the values of the properties by specific reference to the properties
var myRegexp = new RegExp("\\d+","g");
var myRegexpProps = {};
var props = ["source", "flags", "global", "multiline", "sticky", "unicode"];
for (let prop of props) myRegexpProps[prop] = myRegexp[prop];
console.log(myRegexp.source, myRegexp.global, myRegexp.flags
, myRegexp.ignoreCase, myRegexp.multiline);
console.log(myRegexpProps);

ES6 Default Parameters in nested objects

I want to have a function with default parameters inside nested objects, and I want to be able to call it either f() or specifying only individual parameters.
// A function with nested objects with default parameters:
function f({ a = 1, callback = ({ name, param } = { name: "qwe", param: 123 }) } = {}) {
console.log("a:", a);
console.log("callback:", callback);
}
// And I want to run it like this:
f();
f({ callback: { params: "456" } });
// But 'callback.name' becomes undefined.
When destructuring is mixed with default parameters, I admit the code is hard to read and write (especially when there are nested objects...).
But I think you are trying to do that:
function f({callback: {name = "cbFunction", params = "123"} = {}} = {}) {
console.log(name);
console.log(params);
}
f();
f({callback: {params: '789'}});
I found none of the answers here to be what he wanted. But it IS actually possible in a somewhat sexy way by doing this:
(EDIT: Simplified syntax and also show how to add default values for subobjects)
function f({
a = 1,
callback = {}
} = {}) {
callback = { // default values
name: "cbFunction",
params: "123",
...callback // overwrites it with given values
}
// do the same for any subobjects
callback.subObject = {
arg1: 'hi',
arg2: 'hello',
...callback.subObject
}
console.log("a:", a)
console.log("callback:", callback)
}
f()
f({a: 2, callback: {params: '789', subObject: {arg2: 'goodbye'}}})
Turned out to call it like this solves the problem, but is it the best way?
function f({
a = 1,
callback = ({
name,
param
} = {
name: "qwe",
param: 123
})
} = {}) {
console.log("a:", a);
console.log("callback:", callback);
}
f();
f({ callback: { name, params: "456" } });
Answer by #Badacadabra is nearly correct but missing the other top level parameter specified in the question.
function f({a = 1, callback: {name = "qwe", params = "123"} = {}} = {}) {
console.log(a);
console.log(name);
console.log(params);
}
However note that within the function body the properties of callback are addressed without the containing object. You could reconstitute them into such an object if you wanted with the line:
const callback = { name, params }
Either way, from the invocation point this works to fill in all missing values from all levels such as:
f({a: 2})
f({a: 2, callback: { name: "abc"}})
f({a: 2, callback: { params: "456" }})
etc.
EDIT
In response to Joakim's comment:
TotalAMD also said in a comment that "I want to use several nested objects with same fields name". So if he tries that approach with callback1 and callback2 as arguments then he would have to use different field names in them.
I missed that original requirement. One way to maintain the desired, duplicated nested names within the function interface would be to alias them within the scope of the function, as follows:
function f({
a = 1,
callback1: {name: name1 = "abc", params: params1 = "123"} = {},
callback2: {name: name2 = "def", params: params2 = "456"} = {},
} = {}) {
console.log(a);
console.log(name1);
console.log(params1);
console.log(name2);
console.log(params2);
}
You can then call the function with the designed interface and expected results:
f ({ callback1: { name: "One" }, callback2: { name: "Two" } })
Caveat: Whilst technically possible and potentially useful, this could get messy at deeper nesting levels. It might then be worth looking for an alternative interface design with less indirection.

What is a concise way to deep test an object hasOwnProperty (or the like)?

I need to do something with an object. Unfortunately the system I'm doing this with (Titanium) expects values to be non-null, otherwise it will segfault. The input, however, cannot be guaranteed to provide sane objects. Here is an example:
var data = {
foo: {
bar: "foobar"
}
};
function do_it(data) {
do_something_with_non_null_value(data.foo.bar);
}
However it is entirely possible that data is any of the following:
var data = null;
var data = {
foo: null
};
var data = {
foo: {
bar: null
}
};
How can I test for a non-null value in a deep but concise manor to prevent the do_something_with_non_null_value() from crashing?
Underscore.js answers are also welcome.
How about using defaults method from underscore?
function do_it(data) {
var defaults = {
foo: {
bar: null
}
};
data = _.defaults(data || {}, defaults);
do_something_with_non_null_value(data.foo.bar);
}
If data is any of this:
var data = null;
var data = {
foo: null
};
Will change the object to:
var data = {
foo: {
bar: null
}
};

Categories