I'm attempting to allow user defined logic criteria. Since the user definitions are basically strings, I'm trying to make the code efficient by avoiding string comparison. For example, a simple test like a == 10 would be represented by an array
var userDef = ['a', '==', 10]
To make the logic evaluation efficient, I'd like to create an object like the ffg:
var binaryOperator = {'==': 0, '>': 1, '<': 2}
.. so when I mine the array, I could do e.g.
if(binaryOperator[userdef[1]] == 0)
{
return (obj[userDef[0]] == userDef[2])
}
... where of course obj.a = something. The above code, on the face of it, avoids string comparison.
But does it really? Is the reference binaryOperator[userdef[1]] really fast or does it also involve string comparison somewhere?
In other words is there a performance difference between thisObj.a and thisObj['a']?
You could omit the check for the operator and take a function as value, like
var binaryOperator = {
'==': function (a, b) { return a === b; },
'<': function (a, b) { return a < b; },
'>': function (a, b) { return a > b; }
};
Objects in Javascript have an access of O(1).
Further readings:
Performance of key lookup in JavaScript object, JS engine desings
JavaScript object structure: speed matters, by using a fully defined object instead of adding properties at run time.
Related
Why does the comparison operator work faster regardless of the length of string?
Consider the following two functions which I use to compare strings with a length of 100 million:
With ===:
// O(?)
const compare1(a, b) {
return a === b;
}
With character by character comparison:
// O(N)
const compare2(a, b) {
if(a.length !== b.length) return false;
const n = Math.max(a.length, b.length);
for(var i=0; i<n; i++){
if(a[i] !== b[i]) return false;
}
return true;
}
When testing these two functions, I find that the speed of the compare1 function is significantly faster.
In the case of the compare2 function, I think the overhead will be severe in interpreting JavaScript code, accessing and comparing memory.
But from my understanding, the compare1 function may also have to compare N characters. Does it run so much faster because it all happens at a lower level?
There are two considerations:
=== comparison of string primitives is implemented with lower level, compiled code, which indeed executes faster than explicit JavaScript looping, which has additional work, including in updating a JavaScript variable (i), performing an access with that variable (a[i]); where all the prescribed ECMAScript procedures must be followed.
The JavaScript engine may optimise memory usage and use the knowledge that two strings are the same (for instance when that is already detected at parsing time, or one string is assigned to a second variable/property) and only store that string once (cf. String pool). In that case the comparison is a trivial O(1) comparison of two references. There is however no way in JavaScript to inspect whether two string primitives actually share the same memory.
As an illustration of the second point, note how the comparison-time is different for two cases of comparing long strings that are equal -- which probably is a hint that this string pooling is happening:
function compare(a, b) {
let sum = 0, start, p;
for (let i = 0; i < 10; i++) { // Perform 10 comparisons
start = performance.now();
p = a === b;
sum += performance.now() - start;
}
return sum / 10; // Return average time to make the comparison
}
console.log("Hold on while strings are being created...");
setTimeout(function () {
// Create long, non-trivial string
let a = Array.from({length: 10000000}, (_, i) => i).join("");
let b = a.slice(0); // Plain Copy - engine realises it is the same string & does not allocate space
let c = a[0] + a.slice(1); // More complex - engine does not realise it is the same string
console.log("Strings created. Test whether they are equal:", a === b && b === c);
console.log(compare(a, b) + "ms");
console.log(compare(a, c) + "ms");
});
I have a sort function, let's call it mySort(a, b) that returns 1,0,-1 if a is smaller. equal, bigger than b. That way, I can do a myArray.sort(mySort) to sort my array.
I now have a view where the array needs to be sorted in a descending order. My first solution was to just do myArray.sort(mySort).reverse() (yes, I know it's not efficient, but the array contains less than 50 items). But I started thinking that maybe I can just add another parameter to my sort function, that will return the opposite result if set to false: mySort(a, b, ascending = true).
The problem is: if I want to use Array.sort I need to provide a function. Is there a way to use apply (or call?) to create a function that will have ascending set to false, in a way that would allow something like myArray.sort(myFunction.apply(null, [,,false]))? Because I don't see a way to provide a and b on the fly.
You can use mySort.bind() to bind a context and initial arguments, but I don't think there's anything like that that will force later arguments while leaving the first 2 arguments alone.
You can use a wrapper function that adds an argument:
myArray.sort((a, b) => mySort(a, b, true));
or reverses the sign of the result:
myArray.sort((a, b) => -mySort(a, b));
or swaps the argument order:
myArray.sort((a, b) => mySort(b, a));
You can also create a higher order function that implements this.
function reverseArgs(func) {
return function() {
return func.apply(this, Array.from(arguments).reverse());
};
}
myArray.sort(reverseArgs(mySort));
This is an alternate solution, but I'd just wrap your sort function in another function.
function myRevSort(a, b) {
return mySort(a, b) * -1;
}
Or, if you want a boolean flag (although that generally isn't recommended), you can just use it to pick the multiplier, then return a "sorter" function:
function newSorter(ascending = true) {
var mult = ascending ? 1 : -1;
return function(a, b) {
return mySort(a, b) * mult;
}
}
Then use it like:
myArray.sort(newSorter(false));
A note though, I think "comparator" is a more appropriate name for these functions. sort sounds like the function that actually sorts the collection.
Unusual question here and maybe I'm approaching this issue incorrectly -
I'd like to do a comparison between some numeric values in Javascript, but I have the comparators stored in a database (in a VARCHAR field), where specific criteria are stored. (Comparators being <=, <, ==, >, >=, etc).
Is there a way to evaluate the string returned as a comparator in JS?
Thanks
Yes, you could use an object with the comparators as key and return a function for making the comparison.
var comparison = {
'<=': function (a, b) { return a <= b; },
'<': function (a, b) { return a < b; },
'==': function (a, b) { return a == b; },
'>': function (a, b) { return a > b; },
'>=': function (a, b) { return a >= b; },
default: function () { return false; }
}
Usage:
(comparison[comp] || comparison.default)(value1, value2);
There is, but unless you're using it with completely trusted data, don't use it.
Instead, implement the comparisons:
function compare(operator, operand1, operand2) {
switch (operator) {
case "<=":
return operand1 <= operand2;
case ">=":
return operand1 >= operand2;
// ...and so on...
}
}
But yes, if you fully trust the data, you can use eval:
function compare(operator, operand1, operand2) {
return eval("operand1 " + operator + " operand2");
}
(That looks like pseudo-code, but it isn't; eval evaluates the code string you give it in the context where you call it, so it has access to the operand1 and operand2 arguments.)
The "trust" part there is really, really important, because eval allows executing any script code. If you're allowing Bob to provide data that will eventually be evaluated in a browser by Alice, don't use eval. It's a major security risk to Alice.
You can use eval function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
var a = 10;
var b = 20;
var comparator = '<=';
var result = eval(a + comparator + b)
// true
Better approach would be to write your own compare method and keep results/codes on DB something like
function compare(a, b):
if( a < b ) return -2;
else if (a <= b) return -1;
else if ( a == b) return 0;
//...
and then keep values of 0, -1, -2 on db
Now, if you want to apply some comparator from your DB you just call compare on your string and check whether the result is the same as code from DB or not (and it will give you True or False)
The only problem here is that you need to fire all comparisions on dataset but it is completely safe and also will handle some inappropriate operators (that cannot be applied for a strings) - the only requirement is that code on DB must be a key
You can use jquery ajax call to retrieve the string operator from server side code and then evaluate the string for comparison in javascript code.
<script>
$(document).ready(function(){
$.ajax({url: "retrieveOperator.php", success: function(result){
switch(String(result)) {
case "==":
code block for == operator
break;
case ">=":
code block for >= operator
break;
default:
default code block
}
});
});
</script>
I noticed I can use a && b(); as a shorthand for if(a) {b()} or if(a) b();. The MOST usual case for this indecision is for objects having 4-5 elements.
Question: is there a performance gain/loss for using the shorthand when compared with an iteration of if(){} or regular object traversing for (var i in b) { b[i]() } ?
As suggested: some code
var b = { x: 20, y: 30 }, // sample object
fn = function(v){ return v+5; }, // sample function
endValue = 0; // some lol
// go through all elements via if
if ('x' in b) { endValue += fn(b['x']); }
if ('y' in b) { endValue += fn(b['y']); }
//go through all elements via shorthand
('x' in b) && (endValue += fn(b['x']));
('y' in b) && (endValue += fn(b['y']));
//go through all elements via object traversing
for (var i in b) {
endValue += fn(b[i]);
}
I would assume there could be very very negligible performance differences, I need to use the above shorthand method to have a very specific order of the object elements into the computing of endValue.
Is there a performance gain/loss for using the shorthand when compared with an if(){}?
No. With any decent compiler they perform absolutely the same. You should however use the if statements for readability and conciseness.
Is there a performance gain/loss for using an iteration of if(){} vs regular object traversing for (var i in b) { b[i]() }?
That cannot really be compared as they are doing fundamentally different things, and assuming they have the same outcome the performance depends a lot on the actual objects you pass in and their properties. Use the one whose behaviour you need.
closure compiler by google extensively uses shorthand codes and from benchmarks there was minor to none performance gains. https://closure-compiler.appspot.com/home
I have this:
var g = [{a:'a'},{a:'2'},{a:'3'}]
var c = [{a:'4'},{a:'2'},{a:'5'}]
The following statement:
g[1] == c[1]
Returns false, even though the objects look the same. Is there any way I can compare them literally so it will return me true instead of false?
You could encode them as JSON:
JSON.stringify(g[1]) == JSON.stringify(c[1])
You might also be interested in the answers to this related question on identifying duplicate Javascript objects.
For a more complex option, you might look at the annotated source code for Underscore's _.isEqual() function (or just use the library).
The == operator checks for reference equality. The only way to do what you want would be a memberwise equality test on the objects.
This ought to work:
function memberwiseEqual(a, b) {
if(a instanceof Object && b instanceof Object) {
for(key in a)
if(memberwiseEqual(a[key], b[key]))
return true;
return false;
}
else
return a == b;
}
Be wary of cases like these:
var a = {};
a.inner = a; //recursive structure!
here you compare the reference in the memory ,not its value try this and you will get true :
g[1]['a'] === c[1]['a']
In JavaScript variables which are objects (including arrays) are actually references to the collection. So when you write var x = { a: 2 } and var y = { a: 2 } then x and y become references to two different objects. So x will not equal y. But, if you did y = x then they would (because they would share the same reference). But then if you altered either object the other would be altered too.
So, when dealing with objects and arrays, by saying == you are checking if the two references are the same. In this case they are not.