Asynchronous for loop - javascript

Ok, the following code works correctly for me, it is the usual way I do asynchronous loops (count is async). So before callbacking I get 3 numbers logged, in principle different.
var arrayIds = ['a', 'b', 'c'];
var totalIds = arrayIds.length;
var done = 0;
var count = 0;
for (var i = 0; i < arrayIds.length; i++) {
mongoose.Model.count({ 'likes.id': arrayIds[i] }, function (err, c) {
count += c;
console.log(c);
if (++done < totalIds) return; //else
callback(count);
})
}
BUT I don't know what is happening in this other case, with the same philosophy, please help:
var arrayIds = ['a', 'b', 'c'];
var totalIds = arrayIds.length;
var done = 0;
var likesPartial = [];
for (var m = 0; m < arrayIds.length; m++) {
likesPartial.push(arrayIds[m]);
profiles.count({ 'likes.id': { $in: likesPartial } }, function (err, u){
console.log(u);
if (++done < totalIds) return; //else
callback(u);
})
}
The problem is that I get the same 3 numbers logged (with the value of the expected last 'u', the one callbacked in the end), while they should in principle be different, because likesPartial array has at each step a different number of elements.
The two examples seem analogous to me, that's why I can't find the error.
Thanks.

In your first example, you're updating count with each callback and when the last callback comes in, you're calling callback with the final result in count. Which makes sense.
Your second example is markedly different: It receives multiple values but ignores all but the last one (other than logging them), and then calls callback with just the last u it receives. So the previous us it received are thrown away, which doesn't immediately seem to make sense (not least because you can't know which u it is, assuming the operation is asynchronous; they could complete in any order).
Another thing I noted is this:
profiles.count({ 'likes.id': { $in: likesPartial } }, function (err, u){
// ----------------------------^^^^^^^^^^^^^^^^^
You're passing in the array of likesPartial each time, whereas in your first code snippet, you're passing a single ID (arrayIds[i]), not an array.

Related

Maximum calls exceeded somehow

Today I (tried) created some code to create mcq questions.
The code is supposed to generate a random codon, find its amino acid from the codon table/chart and display it on the document along with other (3) random wrong options.
I want to make a mcq with 4 options (1 is correct rest are wrong).
What I am trying to do below is: The computer will form a random sequence of 3 nucleotides (i.e.digits) using form() function. Variable formed will store a codon (eg. UCA, ACC etc.) which will be the question.
Now I declared array arr which will store the correct answer at 0th position.
Then I created a function generateWrongOptions() which will (is supposed to) add the other 3 dissimilar wrong answers to the array. What I tried to do here is that the function will declare a new amino acid (eg. Phe, Ile, Met etc.) which is stored as wrong and a new empty array arr2. The next loop is supposed to check if wrong is already present in arr or not; if it is not then it will push an element 'a' ('a' here doesn't has any meaning) in arr2, if it is then it won't. Now if will check if the arr length is equal to arr2 which simply means if the variable wrong is unique or not (or is duplicate).
I wanted to create 4 options (1 was already present) hence I looped the code for i<3 times.
I found better ways to do this same task online, but those were more advanced and I couldn't understand them. Hence I'd come with my own solution (best I could've guessed).
const obj = {
UUU:"Phe",
UUC:"Phe",
UUA:"Leu",
UUG:"Leu",
CUU:"Leu",
CUC:"Leu",
CUA:"Leu",
CUG:"Leu",
AUU:"Ile",
AUC:"Ile",
AUA:"Ile",
AUG:"Met",
GUU:"Val",
GUC:"Val",
GUA:"Val",
GUG:"Val",
/* - */
UCU:"Ser",
UCC:"Ser",
UCA:"Ser",
UCG:"Ser",
CCU:"Pro",
CCC:"Pro",
CCA:"Pro",
CCG:"Pro",
ACU:"Thr",
ACC:"Thr",
ACA:"Thr",
ACG:"Thr",
GCU:"Ala",
GCC:"Ala",
GCA:"Ala",
GCG:"Ala",
/* - */
UAU:"Tyr",
UAC:"Tyr",
UAA:"Stop",
UAG:"Stop",
CAU:"His",
CAC:"His",
CAA:"Gln",
CAG:"Gln",
AAU:"Asn",
AAC:"Asn",
AAA:"Lys",
AAG:"Lys",
GAU:"Asp",
GAC:"Asp",
GAA:"Glu",
GAG:"Glu",
/* - */
UGU:"Cys",
UGC:"Cys",
UGA:"Stop",
UGG:"trp",
CGU:"Arg",
CGC:"Arg",
CGA:"Arg",
CGG:"Arg",
AGU:"Ser",
AGC:"Ser",
AGA:"Arg",
AGG:"Arg",
GGU:"Gly",
GGC:"Gly",
GGA:"Gly",
GGG:"Gly",
};
const digit = ['U', 'C', 'A', 'G'];
function x() {
return Math.floor(Math.random()*4);
};
function form() {
return digit[x()]+digit[x()]+digit[x()]
}
let formed = form();
let arr = [obj[formed]];
function generateWrongOptions() {
for (i = 0; i < 4; i++) {
let wrong = obj[form()];
let arr2 = [];
for (i = 0; i < arr.length; i++) {
if (wrong!==arr[i]){
arr2.push('a');
};
if(arr2.length==arr.length){
arr.push(wrong)
}
else {
generateWrongOptions()
};
};
};
};
generateWrongOptions();
for (let n of arr) {
console.log(n)
}
Console returns Maximum calls exceeded;
On the other hand a similar code I wrote before creating this - as a guideline - to form an array of 4 different numbers works:
function x() {
return Math.floor(Math.random()*10)
}
let y = x();
let arr = [y];
function aa() {
for (i = 0; i < 4; i++) {
let z = x()
let arr2 = []
for (i = 0; i < arr.length ; i++)
{
if (z!==arr[i])
{arr2.push('a')};
}
if (arr2.length==arr.length)
{arr.push(z)}
else {aa()}
};
};
aa();
console.log(arr);
I think I can fix this code by declaring a new array of all the amino acids in the codon table (obj), but still want to know why the first code doesn't work while the latter does.
I'm not so sure if I understand your code correctly. However, I can see that you have two for loops in which you forgot to create a new variable:
you used "for (i .... )" , and you forgot to say "for (let i ..... )". Another issue i noticed is the redeclaration of "arr" in the last function, which I found weird since you already declared it outside of the function scope. In addition, there is an "arr2" that was also not declared with "let" or "var" words.

LearnYouNode - Jugglling Async (#9) - I Must be Missing Something

There seem to be many questions about this problem out here, but none directly relate to my question AFAICT. Here is the problem statement:
This problem is the same as the previous problem (HTTP COLLECT) in that you need to use http.get(). However, this time you will be provided with three URLs as the first three command-line arguments.
You must collect the complete content provided to you by each of the URLs and print it to the console (stdout). You don't need to print out the length, just the data as a String; one line per URL. The catch is that you must print them out in the same order as the URLs are provided to you as command-line arguments.
Here is my original solution that fails:
var http = require('http')
var concat = require('concat-stream')
var args = process.argv.slice(2, 5)
var args_len = args.length
var results = []
args.forEach(function(arg, i) {
http.get(arg, function(res) {
res.setEncoding('utf8')
res.pipe(concat(function(str) {
results[i] = str
if (results.length === args_len)
results.forEach(function(val) {
console.log(val)
})
}))
}).on('error', console.error)
})
This is the solution they recommend:
var http = require('http')
var bl = require('bl')
var results = []
var count = 0
function printResults () {
for (var i = 0; i < 3; i++)
console.log(results[i])
}
function httpGet (index) {
http.get(process.argv[2 + index], function (response) {
response.pipe(bl(function (err, data) {
if (err)
return console.error(err)
results[index] = data.toString()
count++
if (count == 3)
printResults()
}))
})
}
for (var i = 0; i < 3; i++)
httpGet(i)
What I fail to grok is the fundamental difference between my code and the official solution. I am doing the same as their solution when it comes to stuffing the replies into an array to reference later. They use a counter to count the number of callbacks while I am comparing the length of two arrays (one whose length increases every callback); does that matter? When I try my solution outside the bounds of the learnyounode program it seems to work just fine. But I know that probably means little.... So someone who knows node better than I... care to explain where I have gone wrong? TIA.
They use a counter to count the number of callbacks while I am comparing the length of two arrays (one whose length increases every callback); does that matter?
Yes, it does matter. The .length of an array depends on the highest index in the array, not the actual number of assigned elements.
The difference surfaces only when the results from the asynchronous requests come back out of order. If you first assign index 0, then 1, then 2 and so on, the .length matches the number of assigned elements and would be the same as their counter. But now try out this:
var results = []
console.log(results.length) // 0 - as expected
results[1] = "lo ";
console.log(results.length) // 2 - sic!
results[0] = "Hel";
console.log(results.length) // 2 - didn't change!
results[3] = "ld!";
console.log(results.length) // 4
results[2] = "Wor";
console.log(results.length) // 4
If you would test the length after each assignment and output the array whenever you get 4, it would print
"Hello ld!"
"Hello World!"
So it turns out there were two different issues here, one of which was pointed out by #Bergi above. The two issues are as follows:
The .length method does not actually return the number of elements in the array. Rather it returns the highest index that is available. This seems quite silly. Thanks to #Bergi for pointing this out.
The scoping of the i variable is improper, and as such the value of i can change. This causes a race condition when results come back.
My final solution ended up being as follows:
var http = require('http')
var concat = require('concat-stream')
var args = process.argv.slice(2, 5)
var args_len = args.length
var results = []
var count = 0
function get_url_save(url, idx) {
http.get(url, function(res) {
res.setEncoding('utf8')
res.pipe(concat(function(str) {
results[idx] = str
if (++count === args_len)
results.forEach(function(val) {
console.log(val)
})
}))
}).on('error', console.error)
}
args.forEach(function(arg, i) {
get_url_save(arg, i)
})
Breaking the outtermost forEach into a method call solves the changing i issue since i gets passed in as parameter by value, thus never changing. The addition of the counter solves the issue described by #Bergi since the .length method isn't as intuitive as one would imagine.

Give structure to 'random' function js?

I have an array and a function that picks randomly elements from this array and displays them in a div.
My array:
var testarray = [A, B, C, D, E, F];
Part of the js function:
var new_word = testarray[Math.floor((Math.random()*testarray.length)+1)];
$("#stimuli").text(new_word);
My question is, is there a way I can have them picked randomly in a certain ratio/order?
For example, that if I have my function executed 12 times, that each of the six letters is displayed exactly twice, and that there can never be the same letter displayed twice in a row?
You might want to try a quasi-random sequence. These sequences have the properties you're after. http://en.wikipedia.org/wiki/Low-discrepancy_sequence
Edit:
To your question in the comment: Of course there are hundreds ways to solve a problem. Think about using artificial intelligence, a mathematical algorithm or the answers given by others here. It depends on what you really want to achieve. I just gave a robust solution that is easy to understand and implement..
Here's another (different approach), same result but with the prevention that values displays twice in a row.
Jsfiddle: http://jsfiddle.net/kychan/jJE7F/
Code:
function StructuredRandom(arr, nDisplay)
{
// storage array.
this.mVar = [];
this.previous;
// add it in the storage.
for (var i in arr)
for (var j=0; j<nDisplay; j++)
this.mVar.push(arr[i]);
// shuffle it, making it 'random'.
for(var a, b, c = this.mVar.length; c; a = Math.floor(Math.random() * c), b = this.mVar[--c], this.mVar[c] = this.mVar[a], this.mVar[a] = b);
// call this when you want the next item.
this.next = function()
{
// default value if empty.
if (this.mVar.length==0) return 0;
// if this is the last element...
if (this.mVar.length==1)
{
// we must give it..
return this.mVar.pop();
// or give a default value,
// because we can't 'control' re-occuring values.
return -1;
}
// fetch next element.
var element = this.mVar.pop();
// check if this was already given before.
if (element==this.previous)
{
// put it on top if so.
this.mVar.unshift(element);
// call the function again for next number.
return this.next();
}
// set 'previous' for next call.
this.previous = element;
// give an element if not.
return element;
};
}
NOTE: In this example we can't fully control that the same values are displayed twice.. This is because we can control the first numbers, but when there is only one number left to display, we must either give it or display a default value for it, thus there is a chance that the same value is shown.
Good luck!
Like this?
var arr = [1,2,3,4,5,6,7], // array with random values.
maxDispl = 2, // max display.
arr2 = init(arr) // storage.
;
// create object of given array.
function init(arr)
{
var pop = [];
for (var i in arr)
{
pop.push({value:arr[i], displayed:0});
}
return pop;
}
// show random number using global var arr2.
function showRandom()
{
// return if all numbers has been given.
if (arr2.length<1) return;
var randIndex= Math.floor(Math.random()*arr2.length);
if (arr2[randIndex].displayed<maxDispl)
{
document.getElementById('show').innerHTML+=arr2[randIndex].value + ', ';
arr2[randIndex].displayed++;
}
else
{
// remove from temp array.
arr2.splice(randIndex, 1);
// search for a new random.
showRandom();
}
}
// iterate the function *maxDispl plus random.
var length = (arr.length*maxDispl) + 2;
for (var i=0; i<length; i++)
{
showRandom();
}
jsfiddle: http://jsfiddle.net/kychan/JfV77/3/

Higher Order Functions - Eloquent JS

I have been reading through Chapter 5 last night and throughout the morning and can't seem to get the higher order functions concepts to stick. Here are the examples:
//I understand this first function, I am including it because it is used in the next function.
function forEach(array, action) {
for (vari = 0; i < array.length; i++)
action(array[i]);
}
forEach(["Wampeter", "Foma", "Granfalloon"], print);
function sum(numbers) {
var total = 0;
forEach(numbers, function(number) {
total += number;
});
return total;
}
To my understanding the function sum is taking the argument numbers, which I believe comes in as an array? Now, when the forEach function is called (within sum), it takes the array numbers passed to sum and then it also takes an anonymous function?
I am really confused on what this anonymous function is actually doing. It is taking the parameter number but what else is it doing? Does this anonymous function imply that in that parameter, a function like print or show will be passed the parameter number? In other words it would look something like this
function([10,12,11]) {
var total = 0
forEach([10,12,11]), show(???)
//at this point it would iterate over the array, and use the action passed to display `//the pointer in the array. What I think is happening is that it is taking this pointer value and adding it to the total.` //
I have been trying to wrap my head around this example for a while, if anyone knows of a good explanation or any other documentation to read over I would greatly appreciate it, thanks!
The anonymous function is applied to every currently selected element. You can see better how this works if you unroll (execute stepwise) the loop (pseudocode, * means current element):
var total = 0;
forEach([*1, 2, 3]), fun(1)) => total = 0 + 1 = 1
forEach([1, *2, 3]), fun(2)) => total = 1 + 2 = 3
forEach([1, 2, *3]), fun(3)) => total = 3 + 3 = 6
You can rewrite the sum function like this:
// because there is no "pass by reference" in JavaScript for
// "simple" types, total must be wrapped in an object
// in order to return the sum through the parameter for the showcase
var result = { total: 0 }
function sum(numbers_array) {
for (var i = 0; i < numbers_array.length; i++) {
accumulate(result, numbers_array[i]);
}
}
function accumulate(acc, number) {
acc.total += number;
}
In this case the accumulate function does the same as the anonymous function. When the accumulate function is declared within the scope of the sum function, then the total variable is like global (it is known) to the accumulate function and then there is no need of the first parameter, so the function becomes like the one you already know:
var total = 0;
function sum(numbers_array) {
function accumulate(number) {
total += number;
}
for (var i = 0; i < numbers_array.length; i++) {
accumulate(numbers_array[i]);
}
}
Next step would be to extract and pass the accumulate function as parameter:
var total = 0;
function accumulate(number) {
total += number;
}
// notice, that JavaScript knows how many parameters your function expects
function sum(numbers_array, action) {
for (var i = 0; i < numbers_array.length; i++) {
action(numbers_array[i]);
}
}
What left is to extract the iteration and the code will look like this one in the book.
Let me see if I can explain this easily for you:
The forEach() function accepts two parameters, the first one called array is obviously an array or an array-like object, the second parameter called action is actually a function.
forEach() visits each element in the array passed to it and applies to each element in the array the function passed to it as the second parameter.
So forEach() calls the function passed to it named action for each element in the array and it gives the function the array element as a parameter.
The function sum(numbers) accepts an array as you have though, and it uses forEach() inside itself to calculate the sum of numbers in that array using the anonymous function.
Remeber that the anonymous function is called once for each element in the array passed to sum() so it actually sums the elements in the array.
In simple words : to make your code more generic and concise.
Ex:
Lets say we want to find the max element in an Array :
That's pretty easy and cool :
In java script we will write :
var array = [10,20,30,40,50,60]
function maxEle(array){
var max = array[0];
for(var i=0;i< array.length;i++){
if(max < array[i]){
max = array[i];
}
}
console.log(max);
}
So this will give me the maximum element in an array.
Now after few days, some one asked me that your max is working pretty cool, I want a function which will print the minimum in an array.
Again I will redo the same thing, which i was doing in finding Max.
function minEle(array){
var min = array[0];
for(var i=0;i< array.length;i++){
if(min > array[i]){
min = array[i];
}
}
console.log(min);
}
Now this is also working pretty cool.
After sometime, another requirement comes up : I want a function which will print the sum of all the elements of the array.
Again the code will be similar to what we have written till now, except now it will perform summation.
function sumArr(array){
var sum = 0;
for(var i=0;i< array.length;i++){
sum = sum + array[i];
}
}
console.log(sum);
}
Observation :
After writing these bunch of codes, I m rewriting almost the same thing in every function, iterating over the Array and then performing some action.
Now writing the repetitive code is not a cool stuff.
Therefore we will try to encapsulate the varying part i.e action viz min, max, summation.
Since its feasible to pass functions as arguments to a function in FPL.
therefore we will re-factor our previously written code and now write a more generic function.
var taskOnArr = function(array, task){
for(var i=0;i<array.length;i++){
task(array[i]);
}
}
Now this will be our generic function, which can perform task on each element of Array.
Now our tasks will be :
var maxEle = array[0];
var taskMaxEle = function(ele){
if(maxEle < ele){
maxEle = ele;
}
}
Similarly for min element :
var minEle = array[0];
var taskMinEle = function(ele){
if(minEle > ele){
minEle = ele;
}
}
Also for summation of Array :
var sum = 0;
var taskSumArr = function(ele){
sum = sum + ele;
}
Now we need to pass functions to taskOnArr function :
taskOnArr(array,taskSumArr);
console.log(sum);
taskOnArr(array,taskMinEle);
console.log(minEle);
taskOnArr(array,taskMaxEle);
console.log(maxEle);

Function calls in loops, the parameters of which are updated; to avoid crashing browser

The following is a function for calculating all the possible combinations of a given array:
function combinations(arr, k) {
var i, subI, sub, combinationsArray = [], next;
for (i = 0; i < arr.length; i++) {
if (k === 1) {
combinationsArray.push([arr[i]]);
} else {
sub = combinations(arr.slice(i + 1, arr.length), k - 1);
for (subI = 0; subI < sub.length; subI++) {
next = sub[subI];
next.unshift(arr[i]);
combinationsArray.push(next);
}
}
}
return combinationsArray;
};
For example:
combinations([1,2,3],2);
returns:
[[1,2],[1,3],[2,3]]
I have a nested for loop which modifies a copy of an array of 12 objects (splicing certain elements,depending on the iteration of the loop), before using it as a parameter to the combinations function and storing certain elements of the array returned.
var resultArray = [];
var paramArray = [obj1,obj2,obj3,obj4,obj5,obj6,obj7,obj8,obj9,obj10,obj11,obj12];
for(i=0;i<length1;i++){
for(n=0;n<length2;n++){
paramArray.splice(...);//modifying array
resultArray[n] = combinations(paramArray,2)[i].slice();//storing an element, there are multiples of each element in the resultArray obviously
}
}
The browser crashes with the above type of code
(firefox returns the messege: "A script on this page may be busy, or it may have stopped responding. You can stop the script now, open the script in the debugger, or let the script continue.") The breakpoint is always the part where the combinations function thats being called.
Because the array parameter is different in each iteration, I cant assign the combinations function call to a variable to optimize the code. Is there a more efficient way of writing this?

Categories