This undoubtedly already exists but I can't find it anywhere after 20 min of looking.
All I want is a function that counts up so the first time you call uniqueNum() it returns 0, then 1, then 2 etc.
function uniqueNum(){
if (typeof x !== 'number') {
var x = 0;
} else {
x++;
}
return x
}
I don't want any globals or vars outside of the function hopefully.
What I've got always returns 0;
A closure can do that:
var uniqueNum = (function(){
var num = 0;
return function(){
return num++;
}
}());
uniqueNum(); // 0
uniqueNum(); // 1
A "static" variable works too, as suggested in other answers.
You can implement a "static" variable like this:
function uniqueNum() {
return uniqueNum.counter = (uniqueNum.counter || 0)+1;
}
The following will use the function itself for storing the counter:
function uniqueNum() {
if (uniqueNum.x == null) {
uniqueNum.x = 0;
} else {
uniqueNum.x++;
}
return uniqueNum.x;
}
console.log(uniqueNum()); // 0
console.log(uniqueNum()); // 1
console.log(uniqueNum.x); // 1
Related
I have range function and output functions they works correct,now I want create sum function for using as callbac function in range function,but when some function executed local variable let us say total or sum initialize 0(zero),how can solve this problem?
function range(start,end,callback,step) {
// body...
step=step || 1;
for(i=start;i<=end;i=i+step){
callback(i);
}
}
function output(a) {
// body...
console.log(a);
}
function sum(m){
var total=0;
// some code
}
range(1,5,output);
range(1,5,sum);
function range(start,end,callback,step) {
// body...
var aggregate;
step=step || 1;
for(i=start;i<=end;i=i+step){
aggregate = callback(i, aggregate);
}
}
function output(a) {
// body...
console.log(a);
}
function sum(m, aggregate){
return m + aggregate;
}
range(1,5,output);
range(1,5,sum);
This way you could even do cool stuff like
function conc(m, aggregate) {
return aggregate + m.toString();
}
range(1,5,conc,2); //prints 135
Continuition style code, like you've started it with range(), can get really weird and cumbersome.
And please, please, mind defining your local variables. like i
function range(start,end,callback,step) {
step=step || 1;
for(var i=start; i<=end; i=i+step)
callback(i);
}
function output(...label) {
return function(...args){
console.log(...label, ...args);
}
}
function sum(callback){
var total = 0;
return function(value){
//will log ever intermediate total, because sum() has no way to tell when the sequence is over.
callback(total += +value || 0);
}
}
range(1,5,output('range:'));
range(1,5,sum(output('sum:')));
In this case, I'd prefer using a generator instead, although the higher order functions get obsolete.
function *range(start,end,step) {
step = +step || (end < start? -1: 1);
for(var value = start, count = (end - start) / step; count-- >= 0; value += step)
yield value
}
function sum(iterator){
var total = 0, v;
for(v of iterator) total += +v || 0;
return total;
}
console.log("range:", ...range(1,5))
console.log("sum of range:", sum(range(1,5)))
//just to show that this works with your regular array as well
console.log("sum of array:", sum([1,2,3,4,5]));
//and some candy, as requested by Bergi ;)
//I like to stay with the interfaces as close as possible to the native ones
//in this case Array#reduce
var fold = (iterator, callback, start = undefined) => {
var initialized = start !== undefined,
acc = start,
index = 0,
value;
for(value of iterator){
acc = initialized?
callback(acc, value, index):
(initialized=true, value);
++index;
}
if(!initialized){
throw new TypeError("fold of empty sequence with no initial value");
}
return acc;
}
//and the ability to compose utility-functions
fold.map = (callback, start = undefined) => iterator => fold(iterator, callback, start);
console.log(" ");
var add = (a,b) => a + b; //a little helper
console.log('using fold:', fold(range(1,5), add, 0));
//a composed utility-function
var sum2 = fold.map(add, 0);
console.log('sum2:', sum2( range(1,5) ));
Clearly a range function should not take a callback but be a generator function in modern JavaScript, however you were asking how to write such a callback.
You've already tagged your questions with closures, and they are indeed the way to go here. By initialising a new total within each call of the outer function, you don't need to worry about how to reset a global counter.
function makeSum() {
var total=0;
return function(m) {
total += m;
return total; // so that we can access the result
}
}
var sum = makeSum();
range(1, 5, sum);
output(sum(0));
Won't simply calling the callback on the range array suffice if the callback is not undefined? Like this:
> function range(n, callback) {
const r = [...Array(n).keys()]
if (callback) {
return callback(r)
}
return r
}
> function sum(arr) {
return arr.reduce((a, b) => a + b, 0)
}
> range(10)
> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
> range(10, sum)
> 45
I want to write a function (persistence) that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.
For example:
persistence(39) === 3 // because 3*9 = 27, 2*7 = 14, 1*4=4
// and 4 has only one digit
persistence(999) === 4 // because 9*9*9 = 729, 7*2*9 = 126,
// 1*2*6 = 12, and finally 1*2 = 2
persistence(4) === 0 // because 4 is already a one-digit number
I wrote this:
function persistence(num) {
//code me
var f;
f= countPersistence(num);
var toReturn= f(num); console.log("received value: "+toReturn);
return toReturn;
}
function countPersistence(num){
var count=0;
return function g(num){
var numt=num+"";
numt=numt.split("");
if(numt.length>1){
count++;
for(var i=0; i<numt.length-1; i++){
numt[i+1]=numt[i]*numt[i+1];
}
arguments.callee(numt[numt.length-1]);
}
else
{ console.log("returned value: "+count); return count;}
}
}
As you can see running this code, the returned value of the inner function is not exactly what expected.
Indeed, a function should return to where it is called from, right?. But in this case since it's recursive it is called from itself.
I have no idea how to retrieve the actual value (without using global variable)
You do not return a value when you call your inner function recursively. You could fix it like this (removing the else block and making it common code), so that always the last updated value of count is returned:
function persistence(num) {
//code me
var f;
f= countPersistence(num);
var toReturn= f(num);
return toReturn;
}
function countPersistence(num){
var count=0;
return function g(num){
var numt=num+"";
numt=numt.split("");
if(numt.length>1){
count++;
for(var i=0; i<numt.length-1; i++){
numt[i+1]=numt[i]*numt[i+1];
}
arguments.callee(numt[numt.length-1]);
}
return count;
}
}
console.log(persistence(39)); // 3
console.log(persistence(999)); // 4
console.log(persistence(4)); // 0
But arguments.callee is deprecated, and moreover you are making things overly complicated with nested functions.
You can do it like this:
function persistence(num){
return num < 10 ? 0
: 1 + persistence(String(num).split('').reduce((a, b) => a*b));
}
console.log(persistence(39)); // 3
console.log(persistence(999)); // 4
console.log(persistence(4)); // 0
You are not returning on the recursion line
return arguments.callee(numt[numt.length-1]);
and as I stated in the comments arguments.callee is deprecated so you should use the function name.
return g(numt[numt.length-1]);
I'm trying to make a fallback function that imitates Promise for ie and whatnot
I have the following code:
function goPromise(nr){
console.time("promise");
var sum = 0;
var prom = function(){
return new Promise(function(resolve){
sum = sum + nr;
nr = nr-1;
resolve();
});
}
var doThat = function(){
if(nr > 0){
prom().then(function(){
nr = nr - 1;
doThat()
})
}
else {
console.log(sum);
console.timeEnd("promise");
}
}
doThat();
}
function goNormal(nr){
console.time("normal");
var sum = 0;
var x = function(){
if(nr > 0){
sum = sum + nr;
nr = nr -1;
x();
}
else {
console.timeEnd("normal")
}
}
x();
}
The goNormal works fine and faster than goPromise. That until i give it a big number like 50.000. In which case it gives me this
What does promise have that it can do this stuff no matter how many times?
And how can I implement it in vanilla js ?
A promise is a callback in the future, thus it does NOT recurse. It's like using window.setTimeout (which would solve your issue in the goNormal instance). In the case of the promise, you've set it up and thus the current execution of the "doThat" function actually terminates.
As there is a promise involved, which results in a callback in the future, you'd expect it to be slower. You'll find that using setTimeout will also slow your execution plan down.
The example given is contrived as recursion is not required, however, I get your point. Is there a particular problem you're trying to solve or is this academic?
See my example code below with setTimeout.
function goNormal(nr){
console.time("normal");
var sum = 0;
var x = function(){
if(nr > 0){
sum = sum + nr;
nr = nr -1;
window.setTimeout(x, 10);
}
else {
console.timeEnd("normal")
}
}
x();
}
You can use requestAnimationFrame in order to call the function again, and again, and again ... at roughly 60fps intervals ... and cause it to stop until it meets a certain condition. See simple example:
function recurring() {
var t1 = performance.now();
if (t1 > 1000) {
console.log("Done!");
} else {
console.log("Still going ... ");
window.requestAnimationFrame(recurring);
}
}
window.requestAnimationFrame(recurring);
This post offers two nice solutions that allow you to execute a recursive function as if it was a simple loop.
The first one, the "Trampoline" method:
function trampoline(cb) {
while (cb && cb instanceof Function) {
cb = cb();
}
return cb;
};
function goNormalT(nr) {
console.time("normal");
var sum = 0;
var x = function(){
if(nr > 0){
sum = sum + nr;
nr = nr -1;
return x.bind(null, nr);
}
else {
console.timeEnd("normal");
return null;
}
}
return trampoline(x.bind(null));
};
goNormalT(50000);
And a second one, a "Tail Call Optimizer":
function tco(f) {
var value;
var active = false;
var accumulated = [];
return function accumulator() {
accumulated.push(arguments);
if (!active) {
active = true;
while (accumulated.length) {
value = f.apply(this, accumulated.shift());
}
active = false;
return value;
}
}
}
function goNormal(nr) {
console.time("normal");
var sum = 0;
var x = tco(function() {
if (nr > 0) {
sum = sum + nr;
nr = nr - 1;
return x();
} else {
console.timeEnd("normal");
return null;
}
});
return x();
};
goNormal(50000);
The second one being a bit more sophisticated implementation of the first method.
If the ways these work are hard to grasp, make sure to check the article for an in depth explanation.
Working on a game and cant figure out why my functions aren't working right..
Uncaught RangeError: Maximum call stack size exceeded
By my knowledge one of my functions have a infinite loop(?)
I have a array witch needs to have 3 random speeds in it.
var randomSpeeds = new Array();
I have a function made witch generate a random speed:
function generateSpeed() {
var randomSpeed = Math.random().toFixed(1) * 5;
if(randomSpeed == 0){
randomSpeed = 1;
}
return randomSpeed;
}
The array is filled by this function:
function fillSpeedArray() {
for(var i = 0; i < 3; i++) {
var randomSpeed = generateSpeed();
if(speedArrayChecker(randomSpeed) != false) {
randomSpeeds.splice(i, 0, randomSpeed);
} else {
fillSpeedArray();
}
}
}
The function loops 3 times and each time it generates a random speed and goos through a if/else statement for checking if the array already has got the random generated number (speedArrayChecker).
The speedArrayChecker:
function speedArrayChecker(speed) {
for(speeds in randomSpeeds) {
if(speeds != speed) {
return true;
}
}
return false;
}
This last function goos through the array and if the array already go the random generated number, return true else false.
Function called and checked:
fillSpeedArray();
console.log(randomSpeeds);
By a reason I don't see the functions aren't working correctly.
Thanks and regards.
Solution:
var randomSpeeds = new Array();
function generateSpeed() {
var randomSpeed = Math.random().toFixed(1) * 5;
if(randomSpeed == 0){
randomSpeed = 1;
}
return randomSpeed;
}
function fillSpeedArray() {
while (randomSpeeds.length < 3) {
var randomSpeed = generateSpeed();
if (speedArrayChecker(randomSpeed) != false) {
randomSpeeds.splice(randomSpeeds.length, 0, randomSpeed);
}
}
}
function speedArrayChecker(speed) {
return randomSpeeds.indexOf(speed) === -1
}
fillSpeedArray();
console.log(randomSpeeds);
Explanation:
There were two problems with your code.
The implementation of the speedArrayChecker function was fundamentally flawed. It returned false only when the randomSpeeds was empty.
You had an infinite loop caused by infinite recursion.
The solution was to correct the speedArrayChecker implementation and to use a while loop instead of a for loop.
You have an infinite loop(by recursion) here:
function fillSpeedArray() {
for(var i = 0; i < 3; i++) {
var randomSpeed = generateSpeed();
if(speedArrayChecker(randomSpeed) != false) {
randomSpeeds.splice(i, 0, randomSpeed);
} else {
fillSpeedArray();
}
}
}
In the else if the speed exists already it will call the function it's currently in again and again. This function will never exit, unless you get 3 random speeds perfectly the first time. I would recommend changing fillSpeedArray() to maybe i--.
On a side note why don't you use randomSpeeds.push(randomSpeed) to add speeds to the array.
I want to write a function that checks if the given number has a certain order.
The second number has to be the square of the previous number.
The first number can only be 0 - 9.
So for example 2439 would return 'true' because 4 is the square of 2 and 9 is the square of 3.
39416 would also give 'true', and for example 1624 would return 'false'.
I don't really have an idea how to do this. It should be a recursive function but an example of how to do it without recursion would be helpfull too.
I would try something like this:
function isOrdered(input){
var position = 0;
while(position<input.length-2)
{
var currentFirstNumber = parseInt(input[position]);
if(currentFirstNumber<=2) {
if (Math.sqrt(parseInt(input[position + 1])) !== currentFirstNumber)
return false;
else
position+=2;
}
if(currentFirstNumber>=4 && currentFirstNumber<=9)
{
var squared = input.substring(position+1,position+3);
if(Math.sqrt(parseInt(squared))!==currentFirstNumber)
return false;
else
position=position+3;
}
}
return true;
}
console.log(isOrdered("2439")); // outputs true
console.log(isOrdered("39416")); // outputs true
console.log(isOrdered("1624")); // outputs false
I pass the number to the function as a string.
Take a look at this recursive function
function detectOrder(input)
{
var input = input.toString()
var first = input.substr(0,1);
var power = Math.pow(parseInt(first), 2);
var powerLength = power.toString().length;
if ( parseInt(input.substr(1, powerLength)) == power )
{
if (input.length <= 1+powerLength)
{
return true;
}
else
{
return detectOrder(input.substr(1+powerLength));
}
}
else
{
return false;
}
}
As mention in the comment section, OP said that the 'firsts' are limited to 0..9. So the easiest way to accomplish this is by going through the power function instead of the square root function.
UPDATE: Sorry, you asked for JavaScript code. Be careful with the FIRST CALL. if you manually pass to the function the last position, it will return true.
function verification(number, position){
var str = String(number); // Cast number to String
if(str.length > position){ // Verify valid position
var value = str.substr(position, 1); // take the 'first' value
var pow = Math.pow(value, 2); // Calculate the power
// Verify if the next value is equivalent to the power
if(str.indexOf(pow, position) == position + 1){
// Recursive call to verify the next position
return verification(number, position + String(pow).length + 1);
} else {
// If not equivalent, you found an inconsistency.
return false;
}
// If you ran it until you reached the last position, you're good to go.
}else if(str.length == position){
return true;
}
}
console.log(verification(39416, 0)); // True
console.log(verification(39415, 0)); // True
console.log(verification(981524, 0)); // false
console.log(verification(981525, 0)); // true
console.log(verification(98525, 0)); // false