Executing callback in ascending order - javascript

Attached is the problem and the solution code I wrote. Basically callback should console log the output in ascending order not the order in which it received.
function processRequest(i, callback) {
setTimeout(function() {
callback('Action processed ' + i);
}, Math.random() * 1000);
}
callAction(6);
callAction(count) {
//write code here
}
expected output>>
Action processed 1
Action processed 2
Action processed 3
Action processed 4
Action processed 5
Action processed 6
I know this is not the best way to solve this. Can someone rate my solution and possibly suggest a better way? Something Async I am guessing?
Much appreciated! Thanks.
function processRequest(i, callback) {
setTimeout(function() {
callback('Action processed ' + i);
}, Math.random() * 1000);
}
callAction(6);
function callAction(count) {
const arr = [];
let counter = 0;
for (let i = 1; i <= count; i++) {
arr[i - 1] = i;
}
for (let i = 1; i <= count; i++) {
processRequest(i, function(str) {
counter++;
let currentActionNum = parseInt(str.match(/\d+/g));
let message = str.substr(0, str.indexOf(currentActionNum));
if (currentActionNum === arr[0]) {
console.log(message + currentActionNum);
arr.shift();
}
if (counter === count) {
for (let i = arr[0]; i <= count; i++) {
console.log(message + i);
}
}
});
}
}

Your task is not to use super-complicated processing to rewrite the outputs to appear in the correct order, but to ensure that the 6 processes run sequentially. You can do that with a simple recursive approach:
function callAction(count) {
if (count > 1)
processRequest(7 - count, result => {
// ^^^^^^^^^ ok, that part is a bit weird
console.log(result);
callAction(count - 1);
});
}

I think what the interviewer want you to do is to make sure these processes would run in the right order (the 'real-life' process would be different, not just print some string). So, here is a better solution:
function callAction(count){
i = 1;
cb = function(str){
console.log(str)
count-i++ && processRequest(i, cb) //'same' as: if (count-i++>0) then ...
}
processRequest(1, cb)
}
Just out of curiosity, may I ask if it is a face to face interview? Do you have access to a computer? How long do you have to solve this? If I dont have a computer with me to try and test then I might do worst than that...

Related

Can we change the iteration direction of Array.find?

Look at this crazy question... I have an array with 30.000 items, and I have to run something like this over it:
const bigArray = [
{ createdAt: 1 },
{ createdAt: 2 },
{ createdAt: 3 },
// And so on... 30.000
];
const found = bigArray.find(x => x.createdAt > 29950)
And the thing here, is that I know that 100% of the time, that element will be in the index 29.950 (approx). Because that array is already sorted by createdAt (coming from the backend)
How does .find works? Does it iterates starting from the first element? Is there a way to say "I know it's closer to the end... Change your behavior"?
Of course there is the alternative of doing something like:
bigArray.reverse()
const prevIndex = bigArray.findIndex(x => x.createdAt <= 29950);
const found = bigArray[prevIndex - 1];
bigArray.reverse()
But I'm not sure if that's gonna be actually worst (because of the fact that there we'll also have some multiples unnecessary iterations... I guess).
Who can give me some clues about this?
It's not that I have a bug here... Not even a performance issue (because 30.000 is not that much), but, feels like there should be something there, and I never hear about it in ~16 years working on JavaScript
Thanks so much!
Based upon the documentation here, it appears that find is O(n) time complexity, where n is length of the array.
Since your elements are sorted, you can try to do binary search and reduce time complexity to O(log n).
This is the basic binary search iterative algorithm:
function binarySearchIterative (nums, target) {
let res = -1;
let left = 0;
let right = nums.length;
while (left <= right && res === -1) {
const mid = Math.floor(left + (right - left)/2);
if (nums[mid] === target) {
res = mid;
}
else if (nums[mid] > target) {
right--;
}
else {
left++;
}
}
return res;
};
I'm not aware of any options for Array.prototype.findIndex that start from the end... I do know for sure that using Array.prototype.reverse is very expensive and you could make your own algorithm like this if you know that you're likely to find the result you need near the end:
const bigArray = [
{ createdAt: 1 },
{ createdAt: 2 },
{ createdAt: 3 }
];
// Add the function to Array.prototype
Array.prototype.findIndexFromEnd = function (cond) {
for(let i = this.length - 1; i >= 0; i--) {
if(cond(this[i])) return i;
}
return -1;
}
// Gives 1 as expected
console.log(bigArray.findIndexFromEnd(x => x.createdAt == 2));
// Or use an external function if you don't want to edit the prototype
function findIndexFromEnd(array, cond) {
for(let i = array.length - 1; i >= 0; i--) {
if(cond(array[i])) return i;
}
return -1;
}
// Gives 1 as expected
console.log(findIndexFromEnd(bigArray, (x) => x.createdAt == 2));

Memoize not working as expected?

I'm currently learning about memoization. As a simple exercise I implemented memoization with a fibonacci. However I'm having problems as to why when I do not rename the memoized function it takes slower to complete than when I rename it. Take a look at the code.
This doesn't work correctly and doesn't cache correctly.
function memoize(func) {
const cache = {};
return function(args) {
const cacheKeys = Object.keys(cache).map(el => +el);
if (cacheKeys.includes(args)) {
return cache[args];
}
cache[args] = func(args);
return cache[args];
};
}
function wrapped_fibonacci(n) {
if (n <= 2) {
return 1;
}
return wrapped_fibonacci(n - 1) + wrapped_fibonacci(n - 2);
}
const fibonacci = memoize(wrapped_fibonacci); // <== I do not rename the function.
for (let i = 1; i <= 40; i++) {
console.log(fibonacci(i));
}
However, when I write my code like this. It works correctly and is performant
function memoize(func) {
const cache = {};
return function(args) {
const cacheKeys = Object.keys(cache).map(el => +el);
if (cacheKeys.includes(args)) {
return cache[args];
}
cache[args] = func(args);
return cache[args];
};
}
function fibonacci(n) {
if (n <= 2) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
fibonacci = memoize(fibonacci); //<== I rename the function
for (let i = 1; i <= 40; i++) {
console.log(fibonacci(i));
}
As you can see. I just reassigned the function name.
I'm doing these tests on node.js v8.3.0
The results of the first is as such.
time node fib.js
real 0m2.413s │~
user 0m2.400s │~
sys 0m0.008s
The results of the second goes as such
time node fib.js
real 0m0.263s │~
user 0m0.252s │~
sys 0m0.008s
THATS 1.8S DIFFERENCE
Anyone able to shed some light on this?
In the working example, you're replacing fibonacci with a memoized function also called fibonacci. The recursive calls are using this memoized function, because fibonacci-the-original was replaced by fibonacci-the-memoized.
In the non-working example, you're creating a memoized function fibonacci from wrapped_fibonacci, but that function still calls wrapped_fibonacci, the unmemoized original, recursively.
If you'd also replace wrapped_fibonacci, it would speed up again:
const fibonacci = wrapped_fibonacci = memoize(wrapped_fibonacci)

Get factorial of all the numbers of array recursively

I am trying to get factorial of all the numbers of array(recurArray) using recursion and without loops.
I am getting Error "Maximum call stack size exceeded"
I think there is some issue with the for loop logic, would be helpful if someone can explain the cause of error and how to fix it
Thanks.
//code
function recur(){
var n;
var result;
if(n == 1)
return 1;
var recurArray = [5,6,7,8,9];
for (var i = 0;i<recurArray.length;i++){
n = recurArray[i];
result = n * recur(n-1);
n=n-1;
}
console.log("val of n " + n + "value of i " + i);
return result;
}
recur();
Your recur() function should probably take n as an argument, otherwise n will never be 1 (if(n == 1) return 1;) and your function will keep calling itself until it crashes.
Try function recur(n){ instead.
As you have array, you should use loop.
function recur(x) {
if(x==0) {
return 1;
}
return x * recur(x-1);
}
function getFact() {
var recurArray = [5,6,7,8,9];
for (var i = 0;i<recurArray.length;i++){
console.log(recur(recurArray[i]));
}
}
getFact();
In Each occurence you reset the factorial so it keep rolling for fact(5) you need to have a function that calculate the factorial and an other one for the loop over your array like this :
function recur(n){
if(n == 1){
return 1;
} else {
return n* recur(n-1);
}
}
var recurArray = [5,6,7,8,9];
for (var i = 0;i<recurArray.length;i++){
n = recurArray[i];
result = recur(n);
console.log("factorial of n " + n + " is " + result);
}
Try something like this:
function factorial(number) {
var temp;
if(number <= 1) return 1;
temp = number * factorial(number - 1);
return temp;
}
In this case factorial(5); will return !5 . Recursive functions are not supposed to have loops inside(They can, but the execution time would be horrendous).
Also recursive functions call themselves with different parameters(otherwise you would overflow the browser stack). In your case you call recursive() an infinite amount of times and the loop always starts from 5 , infinitely. The passed parameter is what stops the recursion.

Bluebird promise: why it doesn't timeout?

So, I'm trying to model some long computation. for this purpose I'm computing the fibonacci number. In case when computation takes to much time I need to reject it.
The question: why TimeoutErrror handler doesn't work? How to fix the code?
const expect = require('chai').expect
const Promise = require('bluebird')
function profib(n, prev = '0', cur = '1') {
return new Promise.resolve(n < 2)
.then(function(isTerm) {
if(isTerm) {
return cur
} else {
n = n - 2
return profib(n, cur, strAdd(cur, prev));
}
})
}
const TIMEOUT = 10000
const N = 20000
describe('recursion', function() {
it.only('cancelation', function() {
this.timeout(2 * TIMEOUT)
let prom = profib(N).timeout(1000)
.catch(Promise.TimeoutError, function(e) {
console.log('timeout', e)
return '-1'
})
return prom.then((num) => {
expect(num).equal('-1')
})
})
})
const strAdd = function(lnum, rnum) {
lnum = lnum.split('').reverse();
rnum = rnum.split('').reverse();
var len = Math.max(lnum.length, rnum.length),
acc = 0;
res = [];
for(var i = 0; i < len; i++) {
var subres = Number(lnum[i] || 0) + Number(rnum[i] || 0) + acc;
acc = ~~(subres / 10); // integer division
res.push(subres % 10);
}
if (acc !== 0) {
res.push(acc);
}
return res.reverse().join('');
};
Some info about environment:
➜ node -v
v6.3.1
➜ npm list --depth=0
├── bluebird#3.4.6
├── chai#3.5.0
└── mocha#3.2.0
If I'm reading your code correctly profib does not exit until it's finished.
Timeouts are not interrupts. They are just events added to the list of events for the browser/node to run. The browser/node runs the next event when the code for the current event finishes.
Example:
setTimeout(function() {
console.log("timeout");
}, 1);
for(var i = 0; i < 100000; ++i) {
console.log(i);
}
Even though the timeout is set for 1 millisecond it doesn't appear until after the loop finishes (Which takes about 5 seconds on my machine)
You can see the same problem with a simple forever loop
const TIMEOUT = 10000
describe('forever', function() {
it.only('cancelation', function() {
this.timeout(2 * TIMEOUT)
while(true) { } // loop forever
})
})
Run in with your environment and you'll see it never times out. JavaScript does not support interrupts, it only supports events.
As for fixing the code you need to insert a call to setTimeout. For example, let's change forever loop so it exits (and therefore allows other events)
const TIMEOUT = 100
function alongtime(n) {
return new Promise(function(resolve, reject) {
function loopTillDone() {
if (n) {
--n;
setTimeout(loopTillDone);
} else {
resolve();
}
}
loopTillDone();
});
}
describe('forever', function() {
it.only('cancelation', function(done) {
this.timeout(2 * TIMEOUT)
alongtime(100000000).then(done);
})
})
Unfortunately using setTimeout is really a slow operation and arguably shouldn't be used in a function like profib. I don't really know what to suggest.
The problem appears because promises work in a "greedy" manner(it's my own explanation). For this reason function profib doesn't release event loop. To fix this issue I need to release event loop. The easiest way to do that with Promise.delay():
function profib(n, prev = '0', cur = '1') {
return new Promise.resolve(n < 2)
.then(function(isTerm) {
if(isTerm) {
return cur
} else {
n = n - 2
return Promise.delay(0).then(() => profib(n, cur, strAdd(cur, prev));
}
})
}
gman has already explained why your idea doesn't work. The simple and efficient solution would be to add a condition in your loop that checks the time and breaks, like thus :
var deadline = Date.now() + TIMEOUT
function profib(n, prev = '0', cur = '1') {
if (Date.now() >= deadline) throw new Error("timed out")
// your regular fib recursion here
}
Calling profib will either eventually return the result, or throw an error. However, it will block any other JavaScript from running while doing the calculation. Asynchronous execution isn't the solution here. Or at least, not all of it. What you need for such CPU-intensive tasks is a WebWorker to run it in another JavaScript context. Then you can wrap your WebWorker's communication channel in a Promise to get the API you envisioned originally.

log recursion from jquery function

I'm trying to calculate a series with a recursive function and jQuery but I don't know how to log each recursion that the function is making so I could get the series members.
the code is the following:
$(document).ready(function () {
$("#button").click(function () {
var n = $("#number").val();
function series(n) {
if (n == 1) {
return 6;
} else {
return 0.5 * series(n - 1) + 4;
}
}
console.log(series(n));
});
});
The problem is that the function only logs the last series member. For example if n = 4 the series should be 6, 7, 7.5, 7.75.
The function only returns 7.75.
This is the series formula: series(n) = 0.5 * series(n - 1) + 4, if n = 1 then series(n) = 6;
Thank you!
It's not the most beautiful looking example, but if you take your code and then wrap it in another function with a results array. Then call your inner recursive function and store them to that array it can return the results as an array. You can then use a join to make it into a string to display using jQuery or console log it.
Fiddle: http://jsfiddle.net/mcfarljw/hPWuW/
function getSeriesArray(n) {
var results = [];
function series(n) {
if (n === 1) {
results.push(6);
return 6;
} else {
var result = 0.5 * series(n - 1) + 4;
results.push(result);
return result;
}
}
series(n);
return results;
}
Your use of console.log() is only accepting the output of the outermost series call. If you want to log every iteration you either need to log inside your series method or keep track of every result during the iterations in the series method and then log whatever you used to keep track.
This seems like homework so I wont give too much away, but it might help is used the inspector in browser to walked the execution and get a feel for how the code is flowing.
Try this
$(document).ready(function () {
$("#button").click(function () {
var n = $("#number").val();
function series(n) {
var val=6;
if (n != 1) {
val= 0.5 * series(n - 1) + 4;
}
console.log(val);
return val;
}
});
});

Categories

Resources