Implementing a complicated decision table in JavaScript - javascript

Here's an implementation details question for JavaScript gurus.
I have a UI with a number of fields in which the values of the fields depend in a complicated fashion on the values of seven bits of inputs. Exactly what should be displayed for any one of the possible 128 values that is changing regularly as users see more of the application?
Right now, I've for this being implemented as a decision tree through an if-then-else comb, but it's brittle under the requirements changes and sort of hard to get right.
One implementation approach I've thought about is to make an array of values from 0x0 to 0x7F and then store a closure at each location --
var tbl; // initialize it with the values
...
tbl[0x42] = function (){ doAThing(); doAnotherThing(); }
and then invoke them with
tbl[bitsIn]();
This, at least makes the decision logic into a bunch of assignments.
Question: is there a better way?
(Update: holy crap, how'd that line about 'ajax iphone tags' get in there? No wonder it was a little puzzling.)
Update
So what happened? Basically I took a fourth option, although similar to the one I've checked. The logic was sufficiently complex that I finally built a Python program to generate a truth table in the server (generating Groovy code, in fact, the host is a Grails application) and move the decision logic into the server completely. Now the JavaScript side simply interprets a JSON object that contains the values for the various fields.
Eventually, this will probably go through one more iteration, and become data in a database table, indexed by the vector of bits.
The table driven part certainly came out to be the way to go; there have already been a half dozen new changes in the specific requirements for display.

I see two options...
Common to both solutions are the following named functions:
function aThing() {}
function anotherThing() {}
function aThirdThing() {}
The switch way
function exec(bits) {
switch(bits) {
case 0x00: aThing(); anotherThing(); break;
case 0x01: aThing(); anotherThing(); aThirdThing(); break;
case 0x02: aThing(); aThirdThing(); break;
case 0x03: anotherThing(); aThirdThing(); break;
...
case 0x42: aThirdThing(); break;
...
case 0x7f: ... break;
default: throw 'There is only 128 options :P';
}
}
The map way
function exec(bits) {
var actions = map[bits];
for(var i=0, action; action=actions[i]; i++)
action();
}
var map = {
0x00: [aThing, anotherThing],
0x01: [aThing, anotherThing, aThirdThing],
0x02: [aThing, aThirdThing],
0x03: [anotherThing, aThirdThing],
...
0x42: [aThirdThing],
...
};
in both cases you'd call
exec(0x42);

Since the situation (as you have described) is so irregular, there doesn't seem to be a better way. Although, I can suggest an improvement to your jump table. You mentioned that you have errors and duplicates. So instead of explicitly assigning them to a closure, you can assign them to named functions so that you don't have to duplicate the explicit closure.
var doAThingAndAnother = function (){ doAThing(); doAnotherThing(); }
var tbl; // initialize it with the values
...
tbl[0x42] = doAThingAndAnother;
tbl[0x43] = doAThingAndAnother;
Not that much of an improvement, but it's the only thing I could think of! You seem to have covered most of the other issues. Since it looks like the requirements change so much, I think you might have to forgo elegance and have a design that is not as elegant, but is still easy to change.

Have you considered generating your decision tree on the server rather than writing it by hand? Use whatever representation is clean, easy to work with, and modify and then compile that to ugly yet efficient javascript for the client side.
A decision tree is fairly easy to represent as data and it is easy to understand and work with as a traditional tree data structure. You can store said tree in whatever form makes sense for you. Validating and modifying it as data should also be straight forward.
Then, when you need to use the decision tree, just compile/serialize it to JavaScript as a big if-the-else, switch, or hash mess. This should also be fairly straight forward and probably a lot easier than trying to maintain a switch with a couple hundred elements.

I've got a rough example of a JavaScript decision tree tool if you want to take a look:
http://jsfiddle.net/danw/h8CFe/

Related

o[str] vs (o => o.str)

I'm coding a function that takes an object and a projection to know on which field it has to work.
I'm wondering if I should use a string like this :
const o = {
a: 'Hello There'
};
function foo(o, str) {
const a = o[str];
/* ... */
}
foo(o, 'a');
Or with a function :
function bar(o, proj) {
const a = proj(o);
/* ... */
}
bar(o, o => o.a);
I think V8 is creating classes with my javascript objects. If I use a string to access dynamically a field, will it be still able to create a class with my object and not a hashtable or something else ?
V8 developer here. The answer to "which pattern should I use?" is probably "it depends". I can think of scenarios where the one or the other would (likely) be (a bit) faster, depending on your app's behavior. So I would suggest that you either try both (in real code, not a microbenchmark!) and measure yourself, or simply pick whichever you prefer and/or makes more sense in the larger context, and not worry about it until profiling shows that this is an actual bottleneck that's worth spending time on.
If the properties are indeed known at the call site, then the fastest option is probably to load the property before the call:
function baz(o, str, a) {
/* ... */
}
baz(o, "a", o.a);
I realize that if things actually were this simple, you probably wouldn't be asking this question; if that assumption is true then this is a great example for how simplifications in microbenchmarks can easily change what the right answer is.
The answer to the classes question is that this decision has no impact on how V8 represents your objects under the hood -- that mostly depends on how you modify your objects, not on how you read from them. Also, for the record:
every object has a "hidden class"; whether or not it uses hash table representation is orthogonal to that
whether hash table mode or shape-tracking mode is better for any given object is one of the things that depend on the use case, which is precisely why both modes exist. I wouldn't worry too much about it, unless you know (from profiling) that it happens to be a problem in your case (more often than not, V8's heuristics get it right; manual intervention is rarely necessary).

Conditional data structure (object) access

I have a set of JavaScript functions that handle certain objects. All these objects have the following flexibility:
Fields can be accessed like this: data[prop][sub-prop][etc.], OR
Like this (with a type sub-structure): data[TYPE][prop][sub-prop][etc.].
The object is accessed in many places, and the condition (let's call it is_mixed) is relevant everywhere.
I thought of the following alternatives:
Always access data like this: (is_mixed ? data[TYPE] : data)[prop][sub-prop][etc.]
Have a function called getData and always access data like this: getData()[prop][sub-prop][etc.].
The function code would be:
function getData() { return is_mixed ? data[TYPE] : data; }
Run the following on every new input: if (is_mixed) { data = data[TYPE]; }
It seems to me that options 2 and 3 might be copying the object data (which might be big) and performance is important here (I didn't find the literature to support this guess), but option 1 will make the code big and ugly.
Is there a better option? What's the best way to acheive this in terms of performance, code quality and basically best practices?
It seems to me that options 2 and 3 might be copying the JSON content
No, they won't. They both just copy an object reference, which is quick and cheap (like copying a boolean). #2 is of course slightly slower, since it's a function call, but if it's used a lot, any decent JavaScript engine will inline the function anyway, giving you the benefit of modularity at the source level. (It can take thousands of calls to the function in a shortish period of time to make that kick in, though; e.g., a modern engine only bothers with optimization when it looks likely to matter.)

Getting an enum from backend and use it as a string in javascript

Today I had a rather heathed discussion with a java backend developer. I do the frontend side with jsangular. His rest service sends me an object that includes an enum. I get this as a string and need to show it in an angular grid.
The enums are very self explainable.
Examples what I get:
Person = {gender: "MAN", position: "IS_EMPLOYED"}
In the view I have to show the following:
man is employed
So i solved it in a simple and generic way:
Person.gender.toLowerCase () + ' ' + Person.position.toLowerCase ().replace ('_', ' ')
He found this terrible programming and demanded that I would make a mapper with if else or switch
So: If(Person.gender==="MAN") return "man"
Else if....
The label name is always the same as the enum name (only lower case and space instead of underscore)
He went on a rampage that it was bad coding and basically terrible and a sin against god...
So my question is this bad/good practice (so a b/w story) or rather acceptable and defendable in some cases?
Typed on my phone so I couldn't put the code examples in the right tags..
There are a few ways to look at this:
Mapping is more robust:
He is right in saying that there should be a complete mapping function. This allows you to handle errors (i.e. if the response you get for position is "banana", you should probably not just throw that out there). This is especially important in backend development when you are probably working with a strict schema.
Scale Matters:
There is nothing technically wrong with your approach. If there are a small number of entries or if the user base or application scope is small, it may not matter much. It doesn't really conform to best practices, but it works, and if that is all that matters, it might be enough. Although you should always strive for best practices, you need to keep the big picture in mind.
Efficiency:
One approach may be more efficient than the other. The difference may be negligible. In web dev, speed is a priority, and if both approaches get the same result but one is much faster, choose that one.
It'd probably be a better idea to make a client-enum map - then just send back the actual value. So you could have:
var personTypes: {
"1": "Is Employed",
..
}
Then simply send 1 back as the enum and use the lookup object:
console.log("Person status: " + personTypes[Person.position]);
Which also makes it easier to display a list of types - pick a new one, and simply send the enum back:
<select ng-model="personTypeEnum" ng-options="enum as type for (enum, type) in personTypes"></select>
Check in your console with printing object.
example if object is person then if you are rendering this object as json, gender(enum) would be as
{"success":true,"person":[{"class":"person","id":26,"comment":null,"gender":{"enumType":"com.project.Person$Gender","name":"MEN"},
so u can print person.gender.name, which will give MEN

OOP - Is it better to call functions from within other functions, or to have one big controller?

I'm re-writing a fairly complex script that I created a few weeks ago. After showing it to a few people, they all agreed that I should break it up into smaller pieces (classes) that have a specific purpose, and SOLID object-oriented programming principles
As a self-taught programmer these concepts are pretty new to me, and I'm a little confused about how to best transfer data from one function/class to another. For instance, here is a simplified structure of what I'm building:
MY QUESTION IS:
Should I have a "controller" script that calls all of these functions/classes and passes the data around wherever necessary? Is this where I would put the for loop from the diagram above, even though it would work just as well as part of Function 1 which could then call Function 2?
Without a controller/service script you'll not be able to meet single responsibility principle in fact. Here's a high level design I'd implement if I were you.
FileRepository
Should expose a method to get a file. Let's name it GetFileById.
MongoDBRepository
Methods for CRUD operations. Such as SelectById, SelectByQuery, Update, Create, Delete.
Object creation logic
If your createNewObj logic is simple it would be enough to move it to your object contructor so that your code looks like that: var newObj = new MyObj(json[i]);
But if it's relatively complex maybe because you use some third party frameforks for validation or whatever you'd better create a factory. Then you could would look this way: var newObj = MyObjFactory.Create(json[i]);
Service/controller
Finally you'll need to implement controller/service object. Let's name it WorkService with a method DoWork which should handle all interactions between the repositories and other objects discribed above. Here's approximately DoWork should look like:
function DoWork(fileId){
var json = fileRepository.SelectById(fileId);
for(var i=0;i<json.length;i++){
var newObj = new MyObj(json[i]); // or calling factory method
var dbRecord = MongoDBRepository.SelectByQuery(newObj.name, newObj.date);
if(dbRecord){
MongoDBRepository.Update(newObj);
}
else{
MongoDBRepository.Create(newObj);
}
}
}
Please note it's just a javascript syntax pseudo-code. Your actual code might look absolutley different but it gives your a feeling of what kind of design your should have. Also I exmplicitly didn't create a repository objects inside DoWork method, think of the way to inject them so you meet Dependency inversion principle.
Hope it helps!

Performance & "Better Practice": with statement vs. function with a bunch of parameters

I've been doing a lot of templating in JS lately, so I've invariably run across the "evil" with statement.
It makes templates much easier to work with as you don't have to preface your variables with an object.
Hearing that with statements are bad and also that they may cause poor performance, I set out for another solution:
My Solution: Function with a bunch of parameters
Here's my code:
var locals = {
name : "Matt",
email : "wahoo#wahoo.com",
phone : "(555) 555-5555"
};
var keys = [];
var values = [];
for (key in locals) {
local = locals[key];
keys.push(key)
values.push(local);
}
keys = keys.join(',');
var fn = new Function(keys, "**TEMPLATE STUFF**"); // function(name, email, phone) {...}
fn.apply(this, values); // fn("Matt","wahoo#wahoo.com","(555) 555-5555")
Note: these accomplish the exact same thing. Both are abstracted away from anyone so an obnoxiously long parameter list is no biggie.
I'm wondering which one is better: using a with statement or a function with the potential for a crazy number of parameters.
Unless someone has a better solution...?
Thanks!
Matt Mueller
I find your solution very bloated. It is totally non-trivial, while with is so simple (one line of code which in and of itself has very little cost vs. your object traversal and array instantiations).
Moreover, your solution requires a template object ready when making the templating function (to define its parameters), which may prove down the road less flexible in my opinion.
Check out MDC. A well designed template would presumably have little logic and heavy variable references (and if it isn't that way, then it should be!), which makes with the perfect candidate in such a situation, because there should be very few other lookups in the scope of the with.
Any extra performance that may be gained seems like it would be micro-optimisation, although rather than theorise, just perform some benchmarks. http://jsperf.com/with-vs-fn does all the setup code before the benchmark for your version, but performs the with stuff during the function execution, so it's not really fair, although even on the slowest iterations you get an idea of how fast it is; >400,000 ops/sec is the slowest. I doub't you need to render more than 400,000 templates a second...
Have you tried a JS templating engine? They are usually very fast, and save you some rendering code.
I'm the author of pure.js which is a bit original, but there are plenty of others available and for any taste.
The problems with with are not performance, they are ambiguity and unpredictable behaviour.
See, for example, Hidden Features of JavaScript?

Categories

Resources