I have an array of a couple thousand strings
['7/21/2011', '7/21/2011', '7/21/2011', '7/20/2011', etc]
I am currently, running this code to group by the string and get the max group value:
var max = 0;
var group = {};
arr.map(function (value) {
if (group[value]) {
group[value]++;
} else {
group[value] = 1;
}
max = Math.max(max, group[value]);
});
Are there any improvements to make this code run faster?
EDIT:
The results are in: http://jsperf.com/javascript-array-grouping2
EDIT EDIT: that test was flawed. Mike Samuel's code was the fastest.
6000 entries test -> http://jsperf.com/javascript-array-grouping2
10K entries test -> http://jsperf.com/javascript-array-grouping
If you're sure this is a hotspot and speed is really important, I would try to cut out several thousand function calls by inlining max and map.
You can also make the body of your function faster by cutting out a comparison.
var max = 0;
var group = {};
for (var i = arr.length; --i >= 0;) {
var value = arr[i];
var n = group[value] = 1 - -(group[value] | 0);
if (n > max) { max = n; }
}
The best thing to do is measure on the browsers you care about.
Yes certainly. I would calculate the max last, instead of every iteration, and not use an if:
var group = {};
arr.map(function (value) {
group[value] = (group[value] || 0) + 1;
});
var max = 0;
for (key in group) {
if (group[key] > max) max = group[key];
}
EDIT: As Mike Samuel says you might get faster by using an index instead of map:
var group = {};
var max = 0;
for (var i = arr.length; --i >= 0;) {
group[value] = (group[value] || 0) + 1;
}
for (key in group) {
if (group[key] > max) max = group[key];
}
I think it really depends on the JS engine that you will run this code on. An alternative I think it's worth a shot is using
n = group[value] = (group[value]||0) + 1;
if (n > max) max = n;
for each element.
I also think that may be using a regular loop can be faster because the variables you will use will be just locals and not closed-over variables of a closure (that are normally slower) and you will also save a function call per element. Both those problems are non-issues if the implementation can inline this closure, but I don't know if there are JS implementations smart enough for that.
Related
So, I am trying to write a js function that takes 3 inputs (polynomial, guess and limit) and make them return the approximate root of the polynomial. The problem is that, even with a limit of 1000, the result is still very inaccurate. Does anybody have any ideas on why this may be?
The Method
The code:
var derivativeOfATerm = function(arr) {
var one = arr[0];
var two = arr[1];
var derivative = [];
if (two <= 0) {
return [0, 0];
} else {
derivative.push(one * two);
derivative.push(two - 1);
return derivative;
}
};
var derivativeOfPolynomial = function(arr, order = 1) {
var derivative = [];
for (var i = 0; i < arr.length; i++) {
//console.log(arr[i]);
derivative.push(derivativeOfATerm(arr[i]));
}
if (order === 1) {
return derivative;
} else {
return derivativeOfPolynomial(derivative, order - 1);
}
};
var runPolynomial = function(poly, num) {
var array = [];
for (var i = 0; i < poly.length; i++) {
array.push(Math.pow(num, poly[i][1]) * poly[i][0]);
}
return array.reduce((a, b) => a + b);
};
var newtonRootFind = function(polynomial, guess, limit = 10) {
var derivative = derivativeOfPolynomial(polynomial);
var previous = guess;
var next;
for (var i = 0; i < limit; i++) {
next = previous - (runPolynomial(polynomial, previous)) / (runPolynomial(derivative, previous));
previous = next;
console.log("%o : x=%o, p(x)=%o", i+1, next, runPolynomial(polynomial, next));
}
return previous;
};
console.log("result x=",newtonRootFind([[1,2],[1,1],[-5,0]], 5, 10));
I'm only 12 so try not to use that many technical terms.
For example, entering [[1,2],[1,1],[-5,0]] or x^2+x-5, it returns 1.79128784747792, which isn't accurate enough. It equals 4.79... when it should be very close to 5.
As worked out in the comments, the presented code works as intended, the problem was that in checking the solution x^2 was used for the square x*x.
However, x^y in most C- or Java-like languages is the bitwise "exclusive or", XOR, not the power operation. x^y as symbol for the power operation is usually found in Computer Algebra Systems. Script languages as python or gnuplot tend to use x**y.
What I'm trying to do can be easily illustrated with an example. Assume the following:
var minNum = 1;
var maxNum = 30;
var sum = 75;
var amount = 6;
I want to get all the permutations of amount numbers that add up to sum and are >= minNum && <= maxNum.
For example, if I was to create these permutations by hand, I would start like this:
30,30,12,1,1,1
30,30,11,2,1,1
30,30,11,1,2,1
30,30,11,1,1,2
30,30,10,3,1,1
30,30,10,2,2,1
etc
Is this a known problem in mathematics/programming and are there any algorithms that solve it?
Thanks in advance.
Here's a JavaScript solution (if your environment recursion depth limits the number of results you would like, you can convert the recursion to an explicit array-stack, push the arguments as would a function call and pop to process them):
function partition(n, min, max, parts) {
if (n < 0){
return;
} else if (n == 0) {
document.getElementById('output').innerHTML += (JSON.stringify(parts)) + '<br>';
} else {
for (var i=max; i>=min; i--){
var _parts = parts.slice();
_parts.push(i);
partition(n-i,min,max,_parts)
}
}
}
partition(6,2,4,[])
<pre id="output"></pre>
I think the code(below) is optimized (just use less variables than my initial version of the same logic).
How do I really know if its properly optimized ?
What factors should I consider during optimization ?
Here is the code (
also on jsfiddle )
function process(arr){
var processed = [];
for(var i=0,len=arr.length;i<len;i++){
if(processed.indexOf(arr[i]) < 0){
var nodes = findIndexes(arr,arr[i]);
if(nodes.length > 1){
for(var j=0,jlen=nodes.length;j<jlen;j++){
arr[nodes[j]] = arr[nodes[j]] + '(' + ( j + 1 ) + ')';
}
}
processed.push(arr[i]);
}
}
return arr;
}
function findIndexes(arr,val){
var node = [];
for(var i=0,len=arr.length;i<len;i++){
if(arr[i] === val){
node.push(i);
}
}
return node;
}
// input
var arr = ['aa','bb','bb','aa','cc','dd','cc','ff']
console.log(process(arr));
//output: ["aa(1)", "bb(1)", "bb(2)", "aa(2)", "cc(1)", "dd", "cc(2)", "ff"]
Here is the explanation of the code. 'process' function looks for the same values inside array and for every same values it changes the value by post pending a number to that values, "number" indicates the count of the value as it found in array.
for example
arr = ["x","x","y","z"] will return ["x(1)","x(2)","y","z"]
"y" and "z" are unchanged because they appeared only once.
To optimize I have used an array named as processed that is used to hold values that are just processed inside main for loop, so in next iterations it can be determined that the new iteration value is already processed or not by checking through the array.indexOf method, if the value is already processed then it can safely skip the underlying logic (if/for statements).
Now I have no idea how to further optimize it other than changing the whole process logic.
Optimizations in a broad sense will involve simplifying code, precomputing results which are repeatedly reused, and organizing code so more results can be reused.
Your fiddle code produced following result on analysis.
Logical LOC: 26
Mean parameter count: 3
Cyclomatic complexity: 7
Cyclomatic complexity density: 27%
Maintainability index: 104
Lines of Code (LOC)– Indicates the approximate number of lines in the code. The count is based on the IL code and is therefore not the exact number of lines in the source code file. A very high count might indicate that a type or method is trying to do too much work and should be split up. It might also indicate that the type or method might be hard to maintain.
Maintainability Index – Calculates an index value between 0 and 100 that represents the relative ease of maintaining the code. A high value means better maintainability. Color coded ratings can be used to quickly identify trouble spots in your code. A green rating is between 20 and 100 and indicates that the code has good maintainability. A yellow rating is between 10 and 19 and indicates that the code is moderately maintainable. A red rating is a rating between 0 and 9 and indicates low maintainability.
Cyclomatic Complexity – Measures the structural complexity of the code. It is created by calculating the number of different code paths in the flow of the program. A program that has complex control flow will require more tests to achieve good code coverage and will be less maintainable.
Check code complexities using online tool for your javascript code.
Reference : Link1,Link 2
Javascript optimiser page
Reference(Provides you with different techniques that you should keep in mind while optimising)
You can do it in a single loop:
function process2(arr) {
var out = arr.slice(0),
seen = {},
len = arr.length,
i, key, item, count;
for (i = 0; i < len; ++i) {
key = out[i];
item = seen[key];
if (!item) {
// firstIndex, count
seen[key] = item = [i, 0];
}
count = ++item[1];
if (count > 1) {
if (count === 2) {
out[item[0]] = key + '(1)';
}
out[i] = key + '(' + count + ')';
}
}
return out;
}
// input
var arr = ['aa', 'bb', 'bb', 'aa', 'cc', 'dd', 'cc', 'ff']
console.time('p2');
console.log(process2(arr));
console.timeEnd('p2');
From benchmarking, process2 is approximately 2x faster than process1. That's just a really naive first pass at the problem.
And yet another way to optimize your code with less changes:
In your specific case you go through the whole array for each new found entry although all previous entries have already been processed so it should be possible to opimize further by passing the current index to findIndexes:
function findIndexes(arr,val, fromIndex){
var node = [];
for(var i=fromIndex,len=arr.length;i<len;i++){
if(arr[i] === val){
node.push(i);
}
}
return node;
}
Currrently your code has a O(n^2) complextity. This is caused by your outer loop of arr in process then a call to findIndexes which again loops through arr.
You can simplify this to an O(n) algorithm that loops through the array twice:
function process(arr) {
var result = [];
var counter = {}, counts = {};
var len = arr.length;
for(var i = 0; i < len; i++){
var value = arr[i];
counter[value] = 1;
counts[value] = (counts[value] || 0) + 1;
}
for(var i = 0; i < len; i++){
var value = arr[i];
if(counts[value] == 1) {
result.push(value);
} else {
result.push(value + "(" + counter[value]++ + ")");
}
}
return result;
}
Here's an example that doesn't use nested loops, and uses an object to store key information:
var obj = {};
// loop over the array storing the elements as keys in the object
// if a duplicate element is found, increment the count value
for (var i = 0, l = arr.length; i < l; i++) {
var key = arr[i];
if (!obj[key]) obj[key] = { count: 0, level: 0 };
obj[key].count++;
}
// remove all the key/values where the count is 1
// ie there are no duplicates
for (var p in obj) {
if (obj[p].count === 1) delete obj[p];
}
// for each element in the original array, increase its 'level'
// amend the element with the count
// reduce the count
for (var i = 0, l = arr.length; i < l; i++) {
var key = arr[i];
if (obj[key] && obj[key].count > 0) {
obj[key].level++;
arr[i] = key + '(' + obj[key].level + ')';
obj[key].count--;
}
}
DEMO
What is the most efficient way to write a javascript loop to calculate the number of occurrences of 7's (as an example number) that will be encountered in counting from 1 to 100?
Example:
function numberOccurences(targetNumber, minNumber, maxNumber) {
var count = 0;
for (i = minNumber; i < maxNumber; i++) {
count = count + (i.toString().split(targetNumber).length - 1);
}
return count;
}
var result = numberOccurences(7,1,100);
This will do it without looking at the actual numbers. Sorry, no loop, but you did ask for effeciency. If you really want to use a loop, make the recursion an iteration.
function digitOccurences(digit, min, max, base) {
if (typeof base != "number") base = 10;
return digitOccurencesPlus(digit, max, base, 1, 0) - digitOccurencesPlus(digit, min, base, 1, 0);
function digitOccurencesPlus(digit, N, base, pow, rest) {
if (N == 0) return 0;
var lastDigit = N%base,
prevDigits = (N-lastDigit)/base;
var occsInLastDigit = pow*(prevDigits+(lastDigit>digit));
var occsOfLastInRest = rest * (lastDigit==digit);
// console.log(prevDigits+" "+lastDigit, rest, occsInLastDigit, occsOfLastInRest);
return occsInLastDigit + occsOfLastInRest + digitOccurencesPlus(digit, prevDigits, base, pow*base, pow*lastDigit+rest);
}
}
This is an interesting problem, and already has similar answers for other languages. Maybe you could try to make this one in javascript: Count the number of Ks between 0 and N
That solution is for occurences from 0 to n, but you could easily use it to calculate from a to b this way:
occurences(a,b)= occurences(0,b)-occurences(0,a)
This is much faster (x6) than my original function...JSPERF
function numberOccurences2(targetNumber, minNumber, maxNumber) {
var strMe = "";
for (i = minNumber; i < maxNumber; i++) {
strMe = strMe.concat(i);
}
var re = new RegExp(targetNumber,"g");
var num1 = strMe.length;
var num2 = strMe.replace(re, "").length;
num2 = num1- num2;
return (num2);
}
There has to be a faster way still...
Ok so i need to create four randomly generated numbers between 1-10 and they cannot be the same. so my thought is to add each number to an array but how can I check to see if the number is in the array, and if it is, re-generate the number and if it isnt add the new number to the array?
so basically it will go,
1.create new number and add to array
2.create second new number, check to see if it exist already, if it doesn't exist, add to array. If it does exist, re-create new number, check again etc...
3.same as above and so on.
You want what is called a 'random grab bag'. Consider you have a 'bag' of numbers, each number is only represented once in this bag. You take the numbers out, at random, for as many as you need.
The problem with some of the other solutions presented here is that they randomly generate the number, and check to see if it was already used. This will take longer and longer to complete (theoretically up to an infinite amount of time) because you are waiting for the random() function to return a value you don't already have (and it doesn't have to do that, it could give you 1-9 forever, but never return 10).
There are a lot of ways to implement a grab-bag type solution, each with varying degrees of cost (though, if done correctly, won't ever be infinite).
The most basic solution to your problem would be the following:
var grabBag = [1,2,3,4,5,6,7,8,9,10];
// randomize order of elements with a sort function that randomly returns -1/0/1
grabBag.sort(function(xx,yy){ return Math.floor(Math.random() * 3) - 1; })
function getNextRandom(){
return grabBag.shift();
};
var originalLength = grabBag.length;
for(var i = 0; i < originalLength; i++){
console.log(getNextRandom());
}
This is of course destructive to the original grabBag array. And I'm not sure how 'truly random' that sort is, but for many applications it could be 'good enough'.
An slightly different approach would be to store all the unused elements in an array, randomly select an index, and then remove the element at that index. The cost here is how frequently you are creating/destroying arrays each time you remove an element.
Here are a couple versions using Matt's grabBag technique:
function getRandoms(numPicks) {
var nums = [1,2,3,4,5,6,7,8,9,10];
var selections = [];
// randomly pick one from the array
for (var i = 0; i < numPicks; i++) {
var index = Math.floor(Math.random() * nums.length);
selections.push(nums[index]);
nums.splice(index, 1);
}
return(selections);
}
You can see it work here: http://jsfiddle.net/jfriend00/b3MF3/.
And, here's a version that lets you pass in the range you want to cover:
function getRandoms(numPicks, low, high) {
var len = high - low + 1;
var nums = new Array(len);
var selections = [], i;
// initialize the array
for (i = 0; i < len; i++) {
nums[i] = i + low;
}
// randomly pick one from the array
for (var i = 0; i < numPicks; i++) {
var index = Math.floor(Math.random() * nums.length);
selections.push(nums[index]);
nums.splice(index, 1);
}
return(selections);
}
And a fiddle for that one: http://jsfiddle.net/jfriend00/UXnGB/
Use an array to see if the number has already been generated.
var randomArr = [], trackingArr = [],
targetCount = 4, currentCount = 0,
min = 1, max = 10,
rnd;
while (currentCount < targetCount) {
rnd = Math.floor(Math.random() * (max - min + 1)) + min;
if (!trackingArr[rnd]) {
trackingArr[rnd] = rnd;
randomArr[currentCount] = rnd;
currentCount += 1;
}
}
alert(randomArr); // Will contain four unique, random numbers between 1 and 10.
Working example: http://jsfiddle.net/FishBasketGordo/J4Ly7/
var a = [];
for (var i = 0; i < 5; i++) {
var r = Math.floor(Math.random()*10) + 1;
if(!(r in a))
a.push(r);
else
i--;
}
That'll do it for you. But be careful. If you make the number of random numbers generated greater than the may number (10) you'll hit an infinite loop.
I'm using a recursive function. The test function pick 6 unique value between 1 and 9.
//test(1, 9, 6);
function test(min, max, nbValue){
var result = recursValue(min, max, nbValue, []);
alert(result);
}
function recursValue(min, max, nbValue, result){
var randomNum = Math.random() * (max-min);
randomNum = Math.round(randomNum) + min;
if(!in_array(randomNum, result)){
result.push(randomNum);
nbValue--;
}
if(nbValue>0){
recursValue(min, max, nbValue, result);
}
return result;
}
function in_array(value, my_array){
for(var i=0;i< my_array.length; i++){
if(my_array[i] == value){
console.log(my_array+" val "+value);
return true;
}
}
return false;
}
Here is a recursive function what are you looking for.
"howMany" parameter is count of how many unique numbers you want to generate.
"randomize" parameter is biggest number that function can generate.
for example : rand(4,8) function returns an array that has 4 number in it, and the numbers are between 0 and 7 ( because as you know, Math.random() function generates numbers starting from zero to [given number - 1])
var array = [];
var isMatch= false;
function rand(howMany, randomize){
if( array.length < howMany){
var r = Math.floor( Math.random() * randomize );
for( var i = 0; i < howMany; i++ ){
if( array[i] !== r ){
isMatch= false;
continue;
} else {
isMatch= true;
break;
}
}
if( isMatch == false ){
array.push(r);
ran(howMany, randomize);
}
ran(howMany, randomize);
return array;
}
}
In your answer earlier, you do have a small bug. Instead of
var originalLength = grabBag.length;
for(var i = 0; i < originalLength .length; i++){
console.log(getNextRandom());
}
I believe you meant:
var originalLength = grabBag.length;
for(var i = 0; i < originalLength; i++){
console.log(getNextRandom());
}
Thanks.