I'm working on a web application where an admin can define and adapt formulas that would need to be evaluated based on the input values (numbers) provided by the end users.
For the sake of clarity, here is a reduced example of what I would except:
const obj = {
type: "External wall in contact with the ground",
layer: {
base: {
type: "Reinforced concrete (reinforcement 5 vol.%)",
thickness: 100, // <-- user value
lambda: 2.3, // <-- user value
_r: "{{thickness}}/{{lambda}}/1000", // <-- admin defined
r: 0
},
waterproofing: {
type: "Bitumen sheets (single layer)",
share: 1, // <-- user value
_r: "{{share}}", // <-- admin defined
r: 0,
},
insulation: {
type: "XPS",
thickness: 100, // <-- user value
share: 1, // <-- user value
lambda: 0.040, // <-- user value
_r: "{{thickness}}*{{share}}/{{lambda}}/1000", // <-- admin defined
r: 0
}
}
}
Object.entries(obj.layer).forEach(([key, object]) => {
var formula = object._r
Object.keys(object).forEach(k =>
formula = formula.replace(`{{${k}}}`, object[k])
)
obj.layer[key].r = eval(formula)
})
console.log(obj)
The _r is the formula defined by the admin. The {{value}} are the values provided by the end users.
The loop goes through the obj.layer properties to evaluate the formula and save the answer in r.
The resulting would be this object:
{
type: 'External wall in contact with the ground',
layer: {
base: {
type: 'Reinforced concrete (reinforcement 5 vol.%)',
thickness: 100,
lambda: 2.3,
_r: '{{thickness}}/{{lambda}}/1000',
r: 0.043478260869565216
},
waterproofing: {
type: 'Bitumen sheets (single layer)',
share: 1,
_r: '{{share}}',
r: 1
},
insulation: {
type: 'XPS',
thickness: 100,
share: 1,
lambda: 0.04,
_r: '{{thickness}}*{{share}}/{{lambda}}/1000',
r: 2.5
}
}
}
Let's skip the fact I don't validate the object structure and ensure all the values are available.
I know eval() is considered as "dangerous". A not-so good alternative would be Function(). Yet, not perfect.
So far, I see 3 possibilities:
Only the admin can alter the formulas. Therefore, the risk of executing evil code is very low. What I would need is to validate/sanitize the values (something like isFloat()), and that would be it.
Using the mathjs library, that offers a nice evaluate() function:
const node2 = math.parse('x^a')
const code2 = node2.compile()
let scope = {
x: 3,
a: 2
}
code2.evaluate(scope) // 9
Use a parser generator like http://zaa.ch/jison/, but it seems overkill for what I want to do..
To be honest, I feel the use of eval() in my specific case is justified: dynamic formulas with dynamic values. I could use an external library like mathjs but I feel I don't need it for such simple operations.
I would really like to get your idea on the subject, and hear your suggestion if any !
PS: Yes, the question has been asked already. Although, the most similar questions I found were asked (and responsed) several years ago. I would like to have a fresh input on that matter.
Based on #Sleavely answer, and further reading on the topic, mathjs seems the most reasonable solution to adopt ;-)
Based on #Sleavely answer (see comment quoted below), and further reading on the topic, mathjs seems the most reasonable solution:
Third-party admins may be the only ones who can edit the code, but
where will it actually run? In your backend? On visitors' computers? I
strongly recommend you avoid eval(), lest you be blamed for whatever
Bitcoin miner visitors end up contracting from your application. With
that in mind, I think that you're on the right track with your 2nd
option. Evaluating the formula against a set of predefined variables
strikes me as safe enough. Looks like mathjs actively avoids eval():
https://github.com/josdejong/mathjs/blob/master/docs/expressions/security.md
Related
This question was asked here but no answer was given.
To clarify the question, once a body is created, it is stored in the World/Composite.
The question is, given a body created like so:
Bodies.rectangle(0, 1000, 0, 100, {
isStatic: true,
label: "floor",
friction: 0,
render: {
fillStyle: 'light blue'
},
})
How do you access the body using the label? (Assuming the body is added to the world)
The simple answer is no, there is no built in function that allows you to retrieve a body by its label. The reason is because labels aren't unique and retrieving a body by label can take really long. Imagine if there were thousands of bodies...
In any case, if you still want to retrieve a body by its label you can do this to search for the body in linear time:
// retrieve all bodies in the world and filter on label
// returns an array containing all bodies that have that label
function getBodiesByLabel(label, world) {
return Composite.allBodies(world).filter(body => body.label === label)
}
const floorBodies = getBodiesByLabel('floor')
floorBodies.forEach(floorBody => console.log(floorBody))
If you only have a couple of bodies to look through, it's not that bad.
Source: MatterJS GitHub Question
Credit: grantjenkins on GitHub
The answer by gfdb works, but it involves a linear search over all bodies for each label lookup, O(n). As I mentioned in a couple of comments, MJS does offer a label property for convenience, but doesn't purport to be a holistic entity management solution; it's just a physics engine library. There doesn't seem to be any backing data structure for labels, and that's probably a good thing. Leaning heavily on this single property seems to be an antipattern, expecting MJS to handle entity management when it's not intended to.
So, the general approach when using MJS standalone is to roll your own application-specific entity management solution that meets your needs, or use an opinionated framework like Phaser that offers an off-the-shelf solution.
A couple of common approaches are:
Use a composition pattern: write your own classes and keep fields for MJS bodies as implementation details (probably OK to be tightly-coupled for most use cases), along with whatever other data you need for your app. Group in data structures as needed and optionally inherit from your base classes as in normal OOP.
class Enemy {
constructor(x, y, width, height, opts) {
this.body = Matter.Bodies.rectangle(x, y, width, height, opts);
this.kills = 0;
this.cooldown = 30;
// ... other important data that isn't necessarily MJS-related
}
update() {...}
draw() {...}
...
}
const entities = {
enemies: [new Enemy(...), ...],
walls: [...],
...
};
Use the bodies directly, but put them into an object of arrays organized by label:
const bodiesByType = {
walls: [Matter.Bodies.rectangle(), ...],
enemies: [Matter.Bodies.rectangle(), ...],
players: [Matter.Bodies.rectangle(), ...],
...
};
... or even skip the object and look them up by loose variable names player, walls, etc.
Use gfdb's approach for simple use cases where the above options might be premature optimization (although I don't think option 2 is much work).
Assume I have the following array:
const data = [{
label: 'Östra Halmgatan, Stockholm',
value: 'Östra Halmgatan, Stockholm'
},
{
label: 'Västra Vägen, Stockholm',
value: 'Västra Vägen, Stockholm'
},
{
label: 'Cykelvägen, Göteborg',
value: 'Cykelvägen, Göteborg'
},
{
label: 'Servicevägen, Köpenhamn',
value: 'Servicevägen, Köpenhamn'
}
]}
I want to have a search where the user can search for an item with fuzzy search.
The first step I took was to implement a filter levenshtein algorithm that checks the distance. I filter out all the results with a distance above 2.
The first problem was that when the users start typing, the distance will be way off. If they write ’Serv’ it gives a distance of 19, but it should obviously show the item with Servicevägen, Köpenhamn. I fixed this by only using a substring (from 0 to userInput.length) for the labels instead. This leads to the correct functionality for most cases. So if they write Vöstra (with a typo) it will only show Östra Halmgatan, Stockholm and Västra Vägen, Stockholm (since the distance for those two would be 2 or less).
However, this leads to another issue. If someone would write Stockholm, the first two items should of course be displayed. I fixed this by adding a fix for the filter function, which also checks if the user input value is included in the label string (with all strings set to lowercase).
This works surprisingly well, but I do still have one issue I was wondering if someone can help me figure out.
If someone would be on a non-swedish keyboard for example, I would need to show Göteborg if they were to enter Goteborg. The include doesn't take into account a typo (or a distance) when using the include, only from the start.
Basically, I was wondering if there was a way to write an includes function for strings that takes into account a distance? That would solve all the use cases for my case. Then I could use that function only since Vöstra would return true for the first two (given that the distance is set to 2 or lower). It would also work for Goteborg and Stockholm.
What I'm thinking is you write a function in the style of
String.prototype.includesWithDistance(value: string, maxDistance: number) {
//... how would you approach this?
return substring (with a distance) is included
}
I see a lot of possible cases where this could be used so any ideas are appreciated.
You might want to try out Smith-Waterman Gotoh or Jaro-Winkler algorithm for this kind of calculation. It is more precise and capable of handling typo.
You can test your comparison here:
Test Similarity
I started to make a dungeon-game that creates randomly new dungeons.
Currently I write the function createRoom that returns a new room object based on the arguments. This function is called in createDungeon, but it receivs random parameters there.
function createRoom (width = 1, height = 1, topLeftCoordinate = {x: 0, y: 0}) {
return {
width,
height,
topLeftCoordinate
}
}
function createDungeon (numberOfRooms, rooms = []) {
if (numberOfRooms <= 0) {
return rooms
}
const randomWidth = getRandomNumber(5, 10)
const randomHeight = getRandomNumber(5, 10)
const randomTopLeftCoordinate = {x: getRandomNumber(5, 10), y: getRandomNumber(5, 10)}
return createDungeon (
numberOfRooms - 1,
rooms.concat(Room.create(randomWidth, randomHeight, randomTopLeftCoordinate))
)
}
I don't know if this is the right why, because I don't how to test createDungeon. I can only test if this function retuns an array and the length of the array.. Is this enough or is there a design pattern for the randomness?
Well, first off I'm assuming that your getRandomNumber is in fact a pseudorandom seed-based generator with a global seed. To make it more in the spirit of true FP, you'd need to make the seed/generator passing and mutation explicit, but that's not something you absolutely have to do.
Now, the answer depends on what you want to test. If you need to make sure that your random generation provides the same values for a given seed (which is important when e.g. you want to have "world seeds" like Minecraft does), then it's enough to hardcode the seed and then proceed with known output.
An important note is that when using a global random number generator, every number "drawn out" of it will impact the future numbers. This means that if you change your test code later on to include some other numbers before the previous test cases, your hardcoded values will completely mismatch. You could mitigate that by ensuring that all independent test runs start with a fresh generator.
If you want to test the behavior in a more "reasonable" way, that is, whether the function generally behaves ok, you'll need to use more seeds and run it multiple times. Now whether the seeds are themselves random or hardcoded doesn't matter; the important difference is that your validation rules now can't test for specific value equality, but instead need to check for boundaries or some other range criteria.
In general, test execution should be deterministic and repeatable, so dealing with randomness in tests should be avoided (there's a great book, Lasse Koskela's "Effective Unit Testing", where you can find out how to deal with randomness in tests, among many other issues).
This means, for example, that if you write a test to check what is the result of createDungeon(3), the results of that call should always be the same, so the assertions you make about that result are always valid.
May I suggest a small refactor, in your example: the random number generator should be passed as an argument to createDungeon:
function createDungeon (numberOfRooms, randomGenerator, rooms = []) {
...
const randomWidth = randomGenerator.getRandomNumber(5, 10)
...
}
In your tests, you pass a test double (a mock object) for your randomGenerator, previously set up to return some known values. You can use some mock framework like JsMockito, and then you can just do something like:
generator = mock(RandomGenerator);
when(generator).getRandomNumber(5,10).thenReturn(8);
// run the test
generateDungeon(3, generator);
// verify results
assert(...)
Is there a limitation on the "array" method, specifically an amount of data points required to have it work?
I was using it to display large volumes of data just fine, but when I lowered the amount of points I was plotting, it seemed to break, and only plot one point, with an incorrect X value.
When I switched to use the object notation for data points ({ x: new Date(), y: 100 }) it worked just fine.
To clarify, the two notations seem to function differently with different amounts of data. The only thing different between the below example and my production code are the actual dates/values. Syntax is the same.
[[Date.parse(i1), 100], [Date.parse(i2), 100]]
[{ x: Date.parse(i1), y: 100 }, { x: Date.parse(i2), y: 100 }]
http://api.highcharts.com/highcharts#series.data
The difference, as far as I can say, is that in the array notation, the Date object is not properly interpreted.
Using actual time stamps, both notations work the same.
Neither method should be affected by too few data points.
example:
http://jsfiddle.net/jlbriggs/yPLVP/148/
I have a scenario where there are many many programmers doing very rapid development and we are trying to get more consistency in our labels (words that actually display on the screen for customers). We are in a transition where over the next few years we will be putting a new front-end on to all of our pages. The new front end will be DHTMLX (a JavaScript framework). So now is the perfect time to nail down this new process for consistency.
We basically have 1000s of "key words" that are used many many times throughout 1000s of pages (its a very large product). There has always been slight variations in spelling (especially shorthand). So we would like to come up with a JavaScript structure to hold them all, and the programmer select the proper variable. Something like this...
<script>
/* This "w" structure stands for "word" and would hold all key words */
var w = {
/* This "l" structure stands for "long" and would hold all the long versions */
l: {
ai: "Action Item",
bom: "Bill of Materials",
cage: "CAGE",
assy: "Assembly"
},
/* This "s" structure stands for "short" and would hold all the short versions */
s: {
ai: "AI",
bom: "BOM",
cage: "CAGE",
assy: "Assy"
}
};
//How a programmer would use a label...
w.s.bom
</script>
I know some of you may say this shouldn't be handled in code but rather in the specs phase of development. And you are right. But put that aside for now.
My question is: Can anyone think of a cleaner way to handle this? I just gave just a 4 word example but there will end up being 1000s. I'm also tossing around the idea of two associative arrays (one for long, another for short).
If it were me, I would prefer putting all the variants of a term together, rather than splitting them into different size buckets. If this list was backed by a JSON file that somebody had to maintain, it would make life easier.
Something like this:
var w = {
ai: {
l: "Action Item",
s: "AI"
},
bom: {
l: "Bill of Materials",
s: "BOM"
},
};