Unable to pass an array as argument of a javascript function - javascript

I'm trying to implement the quickSort algorithm in javascript, i have to extract 10,000 numbers from a txt file, pass them into an array, and pass this as an argument of my quickSort function, using the fs module of nodejs. The code is able to read the 10,000 numbers, and to convert them from an array of string to an array of number, but when i try to pass the array into my function, only 3472 numbers are passed, which i don't understand.
const fs = require('fs');
// Reading the data from the file containing the 10,000 numbers
const file = fs.readFileSync('./quickSort.txt', 'utf-8');
//Checking if it has read all the numbers correctly
console.log(file); // Displays the 10,000 numbers as strings in an array
// Convert them from string to integer
const finalFile = file.split('\n').map(e => {
return parseInt(e, 10);
})
// Checking if it has converted each element of the array to an integer
//console.log(finalFile) displays the array, with the 10,000 elements converted to integers
// Initialize a counter for the comparaisons made by the quickSort algorithm
let comparisons = 0;
// Sort them using quick sort
function quick_Sort(origArray) {
if (origArray.length <= 1) {
return origArray;
} else {
// Checking if the array has been correctly passed as an argument
console.log(origArray.length); //Displays 3742 instead of 10,000
var left = [];
var right = [];
var newArray = [];
var pivot = origArray.pop();
var length = origArray.length;
// I have tried comparisons += length - 1; too, but i doesn't work
comparisons += length;
for (var i = 0; i < length; i++) {
if (origArray[i] <= pivot) {
left.push(origArray[i]);
} else {
right.push(origArray[i]);
}
}
for (var i = 0; i < right.length; i++) {
comparisons++;
if (right[i] < pivot) {
return right.splice(i, 0, pivot);
}
}
return newArray.concat(quick_Sort(left), quick_Sort(right));
}
}
// Display the result
const result = quick_Sort(finalFile);
// expected output: 25
console.log(result);
Thank you very much.
Edit: In fact the problem of the size comes from the last for loop of the function, if i delete it, and insert the pivot between like that, it works (thanks to StardustGogeta) :
return newArray.concat(quick_Sort(left), pivot, quick_Sort(right));

This is a logical error. You need to change
return newArray.concat(quick_Sort(left), quick_Sort(right));
to
return newArray.concat(quick_Sort(left), pivot, quick_Sort(right));
With that change, the program works for me. The problem is that you are accidentally getting rid of (via .pop()) approximately 1/3 of your input values (the pivot values) during sorting.

try this:
const finalFile = file.split('\r?\n').map(.....)

Your parsing code works for me except for one issue: parseInt returns NaN for the last new line so you need to remove the last element from the array like this: finalFile.pop();. However this does not explain why you are seeing such a difference in the number of elements. There must be something different either in the code or the file you posted.

Related

indexOf / includes don't do an exact match and return false positives

I want to build an if statement in which the if criteria is based on an equality test of whether a variable equals any of several values. However, I do not want to hardcode the test values, but to pass an array of values that had been randomly subset earlier.
First, I get the set of randomized values by subsetting/sampling 5 values out of an array of 15 values. Basically, I'm using this excellent solution.
function getRandomSubarray(arr, size) {
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(0, size);
}
var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var fiveRandomMembers = getRandomSubarray(x, 5);
Then, I want to pass fiveRandomMembers to test whether a variable is equal to any of the values in fiveRandomMembers's array. Then do something. To this end, I want to use this solution.
var L = function()
{
var obj = {};
for(var i=0; i<arguments.length; i++)
obj[arguments[i]] = null;
return obj;
};
if(foo in L(fiveRandomMembers)) {
/// do something
};
Unfortunately, this doesn't work for me. I must admit that the implementation of this code is within a Qualtrics survey, so the problem might be nuanced to the Qualtrics platform, and that's the reason it isn't working for me. I'm newbie to JavaScript so I apologize if this is a trivial question. But I believe that my code is problematic even in plain JavaScript (that is, regardless of Qualtrics), and I want to figure out why.
UPDATE 2020-05-24
I've been digging into this more deeply, and I have some insights. This looks more like a qualtrics problem rather than plain JS issue. However, the underlying problem might still have to do with some JS mechanism, and that's why I bother to update it here -- maybe someone will know what's causing this behavior.
To recap -- I want to condition an action based on whether a given variable's content matches either of the values in an array. I've tried using both includes and indexOf, but either method fails. The problem boils down to the functions not doing an exact match. For example, if I have an array of 5 numbers such as 8, 9, 12, 13, 14, and I want to test whether 4 exists in the array, then an exact match should return FALSE. However, both indexOf and contains return TRUE because 14 has 4 in it. This is not an exact matching then. Furthermore, I've tried to investigate what is the position indexOf would return for such a false-positive match. Typically, it would return a position that is even larger than the total length of the array, making no sense whatsoever. Here's an example from my Qualtrics survey, demonstrating the problem:
The code giving this is comprised of two qualtrics questions:
(-) First piece
Qualtrics.SurveyEngine.addOnReady(function()
{
/*Place your JavaScript here to run when the page is fully displayed*/
function getRandomSubarray(arr, size) {
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(0, size);
}
var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var fiveRandomMembers = getRandomSubarray(x, 5);
if (Array.isArray(fiveRandomMembers)) Qualtrics.SurveyEngine.setEmbeddedData('is_array', "TRUE");
Qualtrics.SurveyEngine.setEmbeddedData('length', fiveRandomMembers.length);
Qualtrics.SurveyEngine.setEmbeddedData('five_sampled_numbers', fiveRandomMembers);
});
(-) Second piece
Qualtrics.SurveyEngine.addOnReady(function()
{
jQuery("#"+this.questionId).find('.QuestionText:first').css("padding-bottom", "0px");
var currentLoopNum = "${lm://CurrentLoopNumber}";
// var currentLoopNum = parseInt(currentLoopNum, 10); // tried converting to numeric but it doesn't solve the problem
var fiveSampledNumbers = "${e://Field/five_sampled_numbers}";
if (fiveSampledNumbers.includes(currentLoopNum)) {
Qualtrics.SurveyEngine.setEmbeddedData('does_loop_number_appear', "Yes");
} else {
Qualtrics.SurveyEngine.setEmbeddedData('does_loop_number_appear', "No");
}
Qualtrics.SurveyEngine.setEmbeddedData('index_of', fiveSampledNumbers.indexOf(currentLoopNum));
});
Here is a link to the Qualtrics survey, demonstrating the problem, in case it's helpful for troubleshooting: link
However, when testing the same code outside of Qualtrics, the problem doesn't replicate.
Does someone have a clue or even a hypothesis what could be the problem with the matching? Even if you're not necessarily familiar with Qualtrics...
I've never worked with Qualtrics before, but to me it is clear that the line
var fiveSampledNumbers = "${e://Field/five_sampled_numbers}";
will assign a string value to fiveSampledNumbers, not an array value.
Indeed, if you attempt to run the checks you are making on a string rather than an array, you get the unexpected results you saw above, because you are doing string operations rather than array operations:
var fiveSampledNumbers = "6,4,10,11,15";
console.log(fiveSampledNumbers.includes(5)); // logs true (string ends with the character "5")
console.log(fiveSampledNumbers.indexOf(5)); // logs 11 (index of the character "5")
To get around this, you will have to split the string by commas and parse each number within it:
var fiveSampledNumbers = "6,4,10,11,15";
fiveSampledNumbers = fiveSampledNumbers.split(",").map(function (n) { return parseInt(n, 10); });
console.log(fiveSampledNumbers.includes(5)); // logs false
console.log(fiveSampledNumbers.indexOf(5)); // logs -1

Possible bug/misunderstanding of splice and slice

I have the next loop:
rolling_average_delta_follower=[];
followers=[32,34,36,38,40,42,44,46,48,50,52,54,56] // .length = 12
delta_followers=[50,52,54,56,58,60,62,64,66,68,70,72,74] // leng= 12
for (i = 0; i < followers.length ; i++) {
copie = delta_followers.slice(0); //creates duplicate of array delta_followers so I keep source original and not cut from it
copie.splice(7,i) // supposed to create an array that contains numbers from 50 to 64 -> next time the for executes it should go 52 to 66 and so on
console.log(copie)
for (i = 0; i < 8; i++) { // the 7 numbers added previously in the one array are getting summed up
totalx += copie[i]
}
rolling_average_delta_follower.push(totalx) // the sum of each array previously created is getting added to the main array where I need the data.
}
All good until I try to actually execute it, I end up with with a forever loop that I seem not to be able to escape.
Any help would be appreciated.
Thank you!
The problem is here:
for (i = 0; i < 8; i++) { // the 7 numbers added previously in the one array are getting summed up
totalx += copie[i]
}
By this code you override i used in the loop above.
Just use another variable name here. (j ?)
To make a copy of an array use the spread operator.
const copy = [...original];
To sum the values of an array use reduce.
const sum = array.reduce((sum, item) => sum + item, 0);

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);

Generating random unique data takes too long and eats 100% CPU

WARNING: CPU Usage goes to 100%, be careful.
Link to the jsFiddle
This script has been written to design a dynamic snake and ladder board. Everytime the page is refreshed a new board is created. Most of the time all of the background images do not appear, and the CPU usage goes up to 100%. But on occasion all of them appear and the CPU usage is normal.
Opera shows some of the background images, Firefox lags and asks me if I wish to stop the script.
I believe that the problem is with these lines of code:
for(var key in origin) // Need to implement check to ensure that two keys do not have the same VALUES!
{
if(origin[key] == random_1 || origin[key] == random_2 || key == random_2) // End points cannot be the same AND starting and end points cannot be the same.
{
valFlag = 1;
}
console.log(key);
}
Your algorithm is very ineffective. When array is almost filled up, you literally do millions of useless iterations until you're in luck and RNG accidentally picks missing number. Rewrite it to:
Generate an array of all possible numbers - from 1 to 99.
When you need a random numbers, generate a random index in current bounds of this array, splice element and this random position, removing it from array and use its value as your desired random number.
If generated numbers don't fit some of your conditions (minDiff?) return them back to array. Do note, that you can still stall in loop forever if everything that is left in array is unable to fit your conditions.
Every value you pull from array in this way is guaranteed to be unique, since you originally filled it with unique numbers and remove them on use.
I've stripped drawing and placed generated numbers into array that you can check in console. Put your drawing back and it should work - numbers are generated instantly now:
var snakes = ['./Images/Snakes/snake1.png','./Images/Snakes/snake2.jpg','./Images/Snakes/snake3.gif','./Images/Snakes/snake4.gif','./Images/Snakes/snake5.gif','./Images/Snakes/snake6.jpg'];
var ladders = ['./Images/Ladders/ladder1.jpg','./Images/Ladders/ladder2.jpg','./Images/Ladders/ladder3.png','./Images/Ladders/ladder4.jpg','./Images/Ladders/ladder5.png'];
function drawTable()
{
// Now generating snakes.
generateRand(snakes,0);
generateRand(ladders,1);
}
var uniqNumbers = []
for(var idx = 1; idx < 100; idx++){ uniqNumbers.push(idx) }
var results = []
function generateRand(arr,flag)
{
var valFlag = 0;
var minDiff = 8; // Minimum difference between start of snake/ladder to its end.
var temp;
for(var i = 0; i< arr.length; ++i) {
var valid = false
// This is the single place it still can hang, through with current size of arrays it is highly unlikely
do {
var random_1 = uniqNumbers.splice(Math.random() * uniqNumbers.length, 1)[0]
var random_2 = uniqNumbers.splice(Math.random() * uniqNumbers.length, 1)[0]
if (Math.abs(random_1 - random_2) < minDiff) {
// return numbers
uniqNumbers.push(random_1)
uniqNumbers.push(random_2)
} else {
valid = true
}
} while (!valid);
if(flag == 0) // Snake
{
if(random_1 < random_2) // Swapping them if the first number is smaller than the second number.
{
var temp = random_1; random_1 = random_2; random_2 = temp
}
}
else // Ladders
{
if(random_1>random_2) // Swapping them if the first number is greater than the second number.
{
var temp = random_1; random_1 = random_2; random_2 = temp
}
}
// Just for debug - look results up on console
results.push([random_1, random_2])
}
}
drawTable()
I had a problem like this using "HighCharts", in a for loop - "browsers" have an in-built functionality to detect dead scripts or infinite loops. So the browsers halts or pop-ups up a message saying not responding. Not sure if you have that symptom!
This was resulted from a "loop" with a large pool of data. I wrote a tutorial on it on CodeProject, you might try it, and it might be your answer.
http://www.codeproject.com/Tips/406739/Preventing-Stop-running-this-script-in-Browsers

Numeric sort of a stream that keeps alpha order when the number is the same

I tried a few of the regex sorts I found on SO, but I think they may not like the + symbol in the stream i'm needing to sort.
So I'm getting a data stream that looks like this (3 to 30 letters '+' 0 to 64000 number)
userString = "AAA+800|BBB+700|CCC+600|ZZZ+500|YYY+400|XXX+300|XXA+300|XXZ+300";
the output needs to be in the format:
array[0] = "XXA+300" // 300 being the lowest num and XXA being before XXX
array[...]
array[7] = "AAA+800"
I wish to order it from lowest num to highest num and reversed.
Here is my inefficient code. which loops 8x8 times. (my stream maybe 200 items long)
It works, but it looks messy. Can someone help me improve it so it uses less iterations?
var array = userString.split('|');
array.sort();
for(var i=0; i<len; i++) { // array2 contains just the numbers
bits = array[i].split('+');
array2[i] = bits[1];
}
array2.sort();
if(sort_order==2)
array2.reverse();
var c=0;
for(var a=0;a<len;a++) { // loop for creating array3 (the output)
for(var i=0; i<len ; i++) { // loop thru array to find matching score
bits = array[i].split('+');
if(bits[1] == array2[a]) { // found matching score
array3[c++] = bits[0]+'+'+bits[1]; // add to array3
array[i]='z+z'; // so cant rematch array position
}
}
}
array = array3;
Kind Regards
Please forgive the terse answer (and lack of testing), as I'm typing this on an iPhone.
var userArr = userString.split('|');
userArr.sort(function(a, b) {
var aArr = a.split('+'),
bArr = b.split('+'),
aLetters = aArr[0],
bLetters = bArr[0],
aNumbers = parseInt( aArr[1] ),
bNumbers = parseInt( bArr[1] );
if (aNumbers == bNumbers) {
return aLetters.localeCompare( bLetters );
}
return aNumbers - bNumbers;
/*
// Or, for reverse order:
return -(aNumbers - bNumbers);
// or if you prefer to expand your terms:
return -aNumbers + bNumbers;
*/
});
Basically we're splitting on | then doing a custom sort in which we split again on +. We convert the numbers to integers, then if they differ (e.g. 300 and 800) we compare them directly and return the result (because in that case the letters are moot). If they're the same, though (300 and 300) we compare the first parts (XXA and XXX) and return that result (assuming you want an ordinary alphabetical comparison). In this fashion the whole array is sorted.
I wasn't entirely sure what you meant by "and reversed" in your question, but hopefully this will get you started.
As you may've guessed this isn't totally optimal as we do split and parseInt on every element in every iteration, even if we already did in a previous iteration. This could be solved trivially by pre-processing the input, but with just 200 elements you probably won't see a huge performance hit.
Good luck!

Categories