I need an advice for the best practice in order to add behavior to an object received as a json object.
I have a REST services that allow me to define a sort of state machine.
The API define a /sessions resources. When creating a session via POST /sessions/:id I get an json object in my controller:
var session = {
"id": "",
"steps": [ ... ]
}
With this object I would like it to inherit some behavior:
var baseSession = {
"nextStep": function() {... },
"getCurrentStep": function() { ...}
}
So what I would like to do is:
session.__proto__ = baseSession;
But using __proto__ seems not the thing to do.
The other possibility would be to duplicate every property in a new object:
function copyOwnProperyTo(origin, obj) {
Object.keys(origin).forEach(function(prop) {
obj[prop] = origin[prop];
});
}
var newSession = Object.create(baseSession);
copyOwnProperyTo(session, newSession);
This solution work but to me it look a bit heave. Any other suggestions?
The "proper" ES6 solution is to combine Object.assign with Object.create:
var session = Object.assign(Object.create(baseSession), {
"id": "",
"steps": […]
});
Of course you can also use your own copying method instead of Object.assign.
And finally, there is Object.setPrototypeOf, which could be used like
var session = Object.setPrototypeOf({
"id": "",
"steps": […]
}, baseSession);
Related
In my code base I have an object:
let myObject = {
"version": 1,
"traceId": "123456789",
"session": {},
"trigger": {
"service": "some_service_is_here",
"action": "update",
"resource": "transaction"
},
"changesets": [
{
"update": {
"retryCount": 1
},
"conditions": {
"equal": {
"subscriptionId": "aaaaaaaaaaaa",
"transactionId": "bbbbbbbbbbbb"
}
}
},
{
"update": {
"retryCount": 2
},
"conditions": {
"equal": {
"subscriptionId": "ccccccccccccc",
"transactionId": "dddddddddddddd"
}
}
}
]
}
I need to extract some properties from that object. So far it is done using Lodash. There are more extractions going on, but they are fairly identical. Several examples of using it with Lodash:
const trigger = _.get(myObject, 'trigger', null);
const retryCount = _.get(myObject, 'changesets[0].update.retryCount', null);
It works and behaves as expected. I would like to improve this code, by eliminating Lodash and start using Destructuring.
So far I have this:
const trigger = _.get(myObject, 'trigger', null);
becomes
const {trigger} = myObject;
And also:
const retryCount = _.get(myObject, 'changesets[0].update.retryCount', null);
becomes
const {changesets: [{update:{retryCount:retryCount = null}}]} = myObject;
It also works, test are passing:
Now I have several questions:
Is this the correct practice to extract those values?
Is it worth it, in terms of speed and code readability?
The second destructuring example. I will receive an changeset array, that has many objects (unknown number), but I am always interested in the first one. The lodash example illustrates that. When I destructure, I do not specify that I need a first one (zero based) it comes in by default. Do I need somehow to specify I need the zeroth one, or it is default behaviour?
Yes, but currently your variants are not equal. You need to specify null to be default value in variant with destructuring .
Code is not faster. As for readability - it depends.
You currently specify index explicitly by items' position in the list. So for example
const {changesets: [,,{update}]} = myObject;
would explicitly extract 3rd element. You don't need to do anything extra.
PS retryCount:retryCount better don't specify the same name twice. it looks confusing. Reader(like me) will re-read for several times trying to figure out the difference.
Here's my situation, I have a JSON that looks somewhat like this:
{
"items": [{
"type": "condition",
"data": {
"type": "comparison",
"value1": {
"source": "MyType1",
"component": "Attribute1"
},
"value2": {
"source": "MyType2",
"component": "Attribute2"
},
"operator": "gt"
}
},
{
"type": "then",
"data": {
"result": "failed",
"message": "value1 is too high"
}
}
]
}
and would want it to translate to:
if (MyType1.Attribute1 > MyType2.Attribute2) {
result = "failed";
console.log("value1 is too high");
}
Now my problem is, I don't know how I would translate the entries of value1 and value2 to actual code, or rather, how I could access the Object MyType1(maybe through something like getAttribute("MyType1")).
Since I am going to have a whole bunch of sources which each have different components, I cant really write a huge dictionary. Or I would like to avoid it.
The goal is to allow creating if - then - statements via some interactive UI, and I figured it'd be best to save that code as .json files. (Think rule management system).
So, TL,DR, How would I access a Class Attribute this.MyType, if I only have a String MyType to go from? And how would I access the value this.MyType.MyValue, if I get another String MyValue?
Thanks in advance.
Edit:
I'd really like to avoid using eval, for obvious reasons. And if I have to - I guess I would need to create Dictionaries for possible JSON Values, to validate the input?
You need some kind of parser. At first we need some way to store variables and maybe flags:
const variables = {};
var in = false;
Then we go through the code and execute it:
for(const command of items){
switch( command.type ){
case "condition":
//...
case "then":
//...
}
}
To access a variable we can simply do
var current = variables[ identifier ];
To store its the other way round:
variables[ identifier ] = current;
There is an nested object with certain properties which i don't want to be watched. It could be a pattern of properties starting with perhaps "_".
Here's a sample structure.
$scope.ObjectToBeWatched = {
"company": {
"ts": {
"_msg": {"nm":""},
"status": "success"
},
"ids": [
"000000010",
"000000011"
]
},
"_f": [
{
"code": "TY_IO",
"status": "fail"
}
]
}
Standard deep watch:
$scope.$watch("ObjectToBeWatched",function(newObj,oldObj){
},true);
Right now the watch is firing for any any change in any properties which is expected. So in above case any changes to properties
_msg, _f
should not fire.
Thanks for help.
You can try something like this:
$scope.$watch(function($scope) {
return $scope.listOfBigObjects.
map(function(bigObject) {
return bigObject.foo.
fieldICareAbout;
});
}, myHandler, true);
This grabs only the props you care about from the objects in an array. You can use an expression to check for certain field types inside the object map. If you don't have an array just skip that part.
Underscore has tons of functional methods to help w/ this as well if 'map' isn't exactly what you need to return fields you care about.
I have a JSON object which comes back like this from a JavaScript API call:
{
"myArray": [
{
"version": 5,
"permissionMask": 1
},
{
"version": 126,
"permissionMask": 1
}
]
}
How can I access the name of the array (i.e myArray) in JavaScript. I need to use the name of the array to determine the flow later on.
Use getOwnPropertyNames to get a list of the properties of the object in array form.
Example:
var myObj = {
"myArray": [
{
"version": 5,
"permissionMask": 1
},
{
"version": 126,
"permissionMask": 1
}
]
},
names = Object.getOwnPropertyNames(myObj);
alert(names[0]); // alerts "myArray"
Note: If the object can have more than one property, like myArray, myInt, and myOtherArray, then you will need to loop over the results of getOwnPropertyNames. You would also need to do type-testing, as in if(names[0] instanceof Array) {...} to check the property type. Based on your example in your question, I have not fleshed all of that out here.
Object.keys(data)[0]
# => "myArray"
A terminology note: This solution assumes you have a JavaScript object. You might have a JSON string, in which case this is the solution:
Object.keys(JSON.parse(data))[0]
# => "myArray"
However, "JSON object", in JavaScript, is just one - the one I used just now, that has JSON.parse and JSON.stringify methods. What you have is not a JSON object except perhaps in a trivial interpretation of the second case, where all values in JavaScript are objects, including strings.
The other answers are good if you have no control over the return format.
However, if you can, I'd recommend changing the return format to put the important values you care about as actual values instead of keys to make it clearer. For example, something like this:
result =
{
"name: "myArray",
"value": [
{
"version": 5,
"permissionMask": 1
},
{
"version": 126,
"permissionMask": 1
}
]
}
Then, it's a lot clearer to reliably access the property you care about: result.name
I have a network array like the following way
"network_contents": [
{
"facebook":"contents to all pages",
},
{
"twitter":"twiter contents",
},
{
"linkedin":"linked in contents",
}
]
I would like to add some keys to that array bases on its content. If it is facebook the key should be facebook, if it is twitter key should be twitter. But not sure how to do it.
My requirement is to access network array contents, but it may or may not content these facebook, twitter, linked in values. I need to access its values. When i assign a key value will be easy to fetch its contents. So i tried this way to loop through the array
message.network_contents.forEach( function (nwContent) {
if(nwContent.twitter) {
console.log('nw content', nwContent.twitter);
}
})
can i create an array in this foreach loop like the following way.
{
"data": [
{
"facebook": {
"facebook": "facebook content"
},
"twitter": {
"twitter": "twitter content"
}
}
]
}
Your help is much appreciated thanks
Implementation of what I said in the comment:
var oldsies = stuff.network_contents;
var newsies = stuff.network_contents = {};
oldsies.forEach(function(network) {
var name = Object.keys(network)[0];
newsies[name] = network;
});
You gave an example of a JS object and not a dictionary and therefore cant add key-values.
You need something like this:
var network_contents = [];
network_contents["facebook"] = {config1: {name:"config1", value:"value1"}};
network_contents["twitter"] = {config2: {name:"config2", value:"value2"}};
example:
network_contents["facebook"].config1.value; // will return "value1"
You can covert your object to a dictionary easily.