So I have a situation in which I have two pieces of data I am trying to get from the same for loop (well I want it to come out of the same for loop to not have repetitive code).
I am searching for the finalFloor my array of data will take me too; but I am also looking for at what index in address[] the variable currentFloor becomes a negative value.
Below is my code and currently I am running this as two separate functions (floorCalculator & inTheBasement) that run identical code (don't want, not good coding practice) except for the end goal of what is being found. I'm really struggling at trying to figure out how to combine this. Any ideas or pointers? Thanks for the help!
/* ----------------- Declaration of Variables ---------------- */
var up = '('; // represents moving up 1 floor.
var down = ')'; // represents moving down 1 floor.
var input_form = $('#input-form'); // represents the html input form.
var userInput = input_form.find('#address-input'); // represents the finding of the user's input.
var input; // stores user input value.
var address = []; // stores user's input value as an array of characters.
var currentFloor = 0; // represents the current floor in the for loop, set to ground floor (0).
var finalFloor; // represents the ending floor from the instructions given.
var results = $('.results'); // represents the div .results for appending data to.
/* ----------------- Parent Function ---------------- */
$(document).ready(initLoad);
/* ----------------- Child Functions ---------------- */
function initLoad()
{
input_form.submit(function(event) // Listens for submission event at #input-form.
{
event.preventDefault(); // Prevents default method of html element.
takeInAddress(); // Calls function.
});
};
function takeInAddress()
{
input = userInput.val(); // Stores the user input found at #address-input as var input.
userInput.val(''); // Clears the input field for next user input.
address = input.split(''); // Splits the string input into single characters stored now in the array address[ ].
floorCalculator(); // Calls funciton.
};
function floorCalculator()
{
for (var i = 0; i < address.length; i++)
{
if (address[i] == up) // For any '(' present at the current index...
{
currentFloor++; // Increase the value of currentFloor by 1.
}
else if (address[i] == down) // For any ')' present at the current index...
{
currentFloor--; // Decrease the value of currentFloor by 1.
}
} // end for loop
finalFloor = currentFloor; // Store the value of currentFloor now as finalFloor.
// console.log(finalFloor);
results.append('<h2>Floor to deliver to: ' + finalFloor + '</h2>'); // Append finalFloor value to .results html.
inTheBasement(); // Calls function.
};
function inTheBasement()
{
currentFloor = 0; // Resets currentFloor to zero.
for (var i = 0; i < address.length; i++)
{
if (address[i] == up) // For any '(' present at the current index...
{
currentFloor++; // Increase the value of currentFloor by 1.
}
else if (address[i] == down) // For any ')' present at the current index...
{
currentFloor--; // Decrease the value of currentFloor by 1.
if (currentFloor < 0) // if currentFloor becomes a negative value...
{
// console.log(i);
// Append value of i
results.append('<h2>When you will arrive in the basement: ' + i + 'th instruction. </h2>');
break; // break from loop
} // end if loop
} // end else if loop
} // end for loop
};
So, your first loop is a classic use case for "reduce": it turns an array into a single value.
reduce takes a function, and an optional base value:
[1,2,1,1].reduce(aFunction, startValue)
We're going to write a function that, when passed to reduce, adds all values of an array together. The function we pass into reduce should accept two values--a 'memo' that will store state between the function calls, and be passed between them, and a 'value' that will represent the next value into the array, passed one by one. It should return whatever the state is after it has taken the value into account, and whatever it returns will be passed into the function again on the next call, along with the next value in the array.
function aFunction(value, memo) {
return value + memo;
}
startValue = 0; // we start with 0 for our use case
We can make the function syntax shorter, like this:
(memo, value) => value + memo
// the return statement is implicit in this syntax
As a result, passing our function and our start value becomes a one liner:
[1,2,1,1].reduce((memo, value) => value + memo, 0)
The only other piece of knowledge necessary is the ternary:
(memo, value) => value === ")" ? memo + 1 : memo - 1
The above is equivalent to:
function (memo, value) {
if (value === ")") {
return memo + 1;
}
else {
return memo - 1;
}
}
Finally, if we want to do this all in one reduce call, we just need to pass a little more state along in our memo, and do another evaluation.
ourInput = ")()()((())))))()()()(".split("");
// it's now an array, as you know
state = { floor: 0, basementTrigger: false, becameNegative: undefined };
result = ourInput.reduce( (memo, value, index) => {
memo.floor += value === "(" ? 1 : -1; // add either 1 or negative one to our floor
if (!memo.basementTrigger && memo.floor < 0) {
memo.becameNegative = index
memo.basementTrigger = true;
}
return memo;
}, state) // state is passed in as 'memo' on the inner functions's first call
For every value, this:
either adds or subtracts from floor, based on whether value is "(".
if the trigger is false, and the floor is negative, it:
flips the trigger to true, and stores the current index
then we just add:
output += ("result = " + result.floor);
if (result.basementTrigger) output += ("follow instruction: " + result.becameNegative)
Hopefully this helps on an alternative take to the question.
disclaimer: did not proofread or test code, may be mistakes; my goal isn't to give you code anyways, but to show you concepts. This is a hacky rendering thrown up quickly, but should illustrate the tools you can use on your own.
Check for currentFloor < 0 in the first for loop. To keep from printing the message twice, use a variable to remember if you already did it.
function floorCalculator()
{
var foundBasement = false;
var basementStep;
for (var i = 0; i < address.length; i++)
{
if (address[i] == up) // For any '(' present at the current index...
{
currentFloor++; // Increase the value of currentFloor by 1.
}
else if (address[i] == down) // For any ')' present at the current index...
{
currentFloor--; // Decrease the value of currentFloor by 1.
if (currentFloor < 0 && !foundBasement) {
foundBasement = true;
basementStep = i;
}
}
} // end for loop
finalFloor = currentFloor; // Store the value of currentFloor now as finalFloor.
// console.log(finalFloor);
results.append('<h2>Floor to deliver to: ' + finalFloor + '</h2>'); // Append finalFloor value to .results html.
if (foundBasement) {
results.append('<h2>When you will arrive in the basement: ' + basementStep + 'th instruction. </h2>');
}
};
Related
I am coding in javascript and am trying to make a sorting algorithm, and my while loop doesnt seem to exit, anybody know why?
let sort = true
let length = 0
let i = 1
function multiSort(n) {
length = n.length-1
while (sort=true) {
sort = false
if (n[i]>n[i+1]) {
[n[i],n[i+1] = n[i+1],n[i]]
i += 1
sort=true
if (i = length) {
i = 1
}
console.log(i)
}
}
return n
}
console.log("Final Product: ", multiSort([3,2,5,1,4]), " Iterations: ", i)
You have a couple problems here.
As mentioned your using javascript incorrectly. You should spend some time learning about how operators and comparisons work. You can read about them here. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#assignment_operators
Your while statement will never be false.
n[i+1] in your compare statement can be undefined so will be false when it shouldn't be checked.
I have re-written your algorithm using a for loop instead of a while loop since we get a free iterator when we set up the loop.
// We keep track of the iterations outside of the function
let iterations = 0
function multiSort(n) {
iterations++
// We set sort to false, if our conditions are not met we won't need to keep sorting
let sort = false
// Loop through every item in the array
for(let i = 0; i < n.length; i++) {
let next = n[i+1]
// If the current item is larger than the next swap their positions. We also have to check for undefined here since the end of the array can't compare to anything.
if(n[i] > next && next !== undefined) {
// Set sort to true since at least one item need to be sorted. This will not be set if all items in the array are in sequence.
sort = true
n[i+1] = n[i]
n[i] = next
}
}
// If we set sort to true re-run everything with the current array.
if(sort) {
multiSort(n)
}
// return the final array
return n
}
console.log("Final Product: ", multiSort([3,2,5,1,4]), " Iterations: ", iterations)
I'm creating a 2-dimensional heat map which has functionality when you click on any pixel. It grabs data associated with the index of every pixel (including adjacent pixels) and plots it. It currently looks like this:
The problem that I'm encountering is when I click on a left or right edge pixel, since it grabs data from adjacent pixels, it can retrieve data from the opposite side of the graph since it is all within a one-dimensional array. I am trying to create a conditional which checks if the clicked pixel is an edge case, and then configures the magnified graph accordingly to not show points from the other side of the main graph. This is the code I have so far:
// pushes all dataMagnified arrays left and right of i to magMainStore
var dataGrabber = function(indexGrabbed, arrayPushed) {
// iterates through all 5 pixels being selected
for (var b = -2; b <= 2; b++) {
var divValue = toString(i / cropLength + b);
// checks if selected index exists, and if it is not in the prior row, or if it is equal to zero
if (dataMagnified[indexGrabbed + b] != undefined && (& divValue.indexOf(".")!=-1)) {
dataMagnified[indexGrabbed + b].forEach(function(z) {
arrayPushed.push(z);
})
}
}
};
I am trying to get the same result as if I had a two dimensional array, and finding when the adjacent values within a single array is undefined. This is the line where I'm creating a conditional for that
if (dataMagnified[indexGrabbed + b] != undefined && (& divValue.indexOf(".")!=-1)) {
The second condition after the and is my attempts so far trying to figure this out. I'm unsure if I can even do this within a for loop that iterates 5 times or if I have to create multiple conditions for this. In addition, here's an image displaying what I'm trying to do:
Thank you!
Your approach looks overly complex and will perform rather slowly. For example, converting numbers to strings to be able to use .indexOf() to find a decimal point just for the sake of checking for integer numbers doesn't seem right.
A much simpler and more elegant solution might be the following function which will return the selection range bounded by the limits of the row:
function getBoundedSelection(indexGrabbed, selectionWidth) {
return dataMagnified.slice(
Math.max(Math.floor(indexGrabbed/cropLength) * cropLength, indexGrabbed - selectionWidth),
Math.min(rowStartIndex + cropLength, indexGrabbed + selectionWidth)
);
}
Here, to keep it as flexible as possible, selectionWidth determines the width of the selected range to either side of indexGrabbed. This would be 2 in your case.
As an explanation of what this does, I have broken it down:
function getBoundedSelection(indexGrabbed, selectionWidth) {
// Calculate the row indexGrabbed is on.
var row = Math.floor(indexGrabbed/cropLength);
// Determine the first index on that row.
var rowStartIndex = row * cropLength;
// Get the start index of the selection range or the start of the row,
// whatever is larger.
var selStartIndex = Math.max(rowStartIndex, indexGrabbed - selectionWidth);
// Determine the last index on that row
var rowEndIndex = rowStartIndex + cropLength;
// Get the end index of the selection range or the end of the row,
//whatever is smaller.
var selEndIndex = Math.min(rowEndIndex, indexGrabbed + selectionWidth);
// Return the slice bounded by the row's limits.
return dataMagnified.slice(selStartIndex, selEndIndex);
}
So I discovered that since the results of the clicked position would create a variable start and end position in the for loop, the only way to do this was as follows:
I started the same; all the code is nested in one function:
var dataGrabber = function(indexGrabbed, arrayPushed) {
I then create a second function that takes a start and end point as arguments, then passes them as the for loop starting point and ending condition:
var magnifyCondition = function (start, end) {
for (var b = start; b <= end; b++) {
if (dataMagnified[indexGrabbed + b] != undefined) {
dataMagnified[indexGrabbed + b].forEach(function (z) {
arrayPushed.push(z);
})
}
}
};
After that, I created 5 independent conditional statements since the start and end points can't be easily iterated through:
if (((indexGrabbed - 1) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-1, 2);
}
else if ((indexGrabbed / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(0, 2);
}
else if (((indexGrabbed + 1) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-2, 0);
}
else if (((indexGrabbed + 2) / cropLength).toString().indexOf(".") == -1) {
magnifyCondition(-2, 1);
}
else {
magnifyCondition(-2, 2);
}
};
Lastly, I pass the index grabbed (i of the on clicked function) and an arbitrary array where the values get stored.
dataGrabber(i, magMainStore);
If there's a better way instead of the if statements, please let me know and I'd be happy to organize it better in the future!
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/
I suppose this is a newbie question, but I can't seem to figure it out. I have this code, from eloquent javascript, about the reduce function:
function forEach ( info, func ) {
for ( i = 0; i < info.length; i++) {
func(info[i]);
}
}
function reduce (combine, base, array) {
forEach(array, function(elem) {
base = combine(base, elem);
console.log("The base is: " + base);
});
return base;
}
function countZeroes(array) {
function counter(total, element) {
console.log("The total is: " + total);
return total + (element === 0 ? 1 : 0);
}
return reduce(counter, 0, array);
}
What I can not figure out is, how is the number of zeroes stored in total through each call of the function? Why does it keep a running tab, instead of getting wiped out each time?
The structure of reduce is that it applies a function f which takes two operands - here called element and total to a sequence. element is the next unprocessed item in the sequence (array); total is the result of the previous call to f.
Conceptually reduce(f, 0, [1,2,3]) expands to f(3,f(2,f(1,0).
Now, to answer your question: the running total is stored between invocations of counter in the variable base inside reduce.
I've a problem with set a limit into my own lightbox for a gallery
<script>
var imagenumber = 0;
function btnleft(){
load = imagenumber-=1;
document.getElementById('lightboxcontent').innerHTML=imagelist[load];
}
function btnright(){
load = imagenumber+=1;
if (load==undefined){load=imagenumber-=1}
document.getElementById('lightboxcontent').innerHTML=imagelist[load];
}
</script>
Then the array
var imagelist=new Array(); // regular array (add an optional integer
imagelist[0]="image1.jpg"; // argument to control array's size)
imagelist[1]="image2.jpg";
imagelist[2]="image3.jpg";
When I click more then 3 times on the next button I got the error-message "undefined".
How should I do to get a limit on my arrays?
Try it with
function btnleft(){
var load = imagelist[imagenumber-=1];
if (load) // imagenumber in array boundaries
document.getElementById('lightboxcontent').innerHTML = load;
else
imagenumber = 0;
}
function btnright(){
var load = imagelist[imagenumber+=1];
if (load) // imagenumber in array boundaries
document.getElementById('lightboxcontent').innerHTML = load;
else
imagenumber = imagelist.length-1;
}
Yet, Arrays in Javascript have no limited size, they are more like (infinite) lists. You can hardly set a limit on their length - espcially not with the constructor, whose number argument is just for initialisation purposes.
You can use the length property of an array to check whether your index is in the array boundaries: i >= 0 && i < arr.length. My code just checks whether there is an item at that index (as your second function seems to intend, too) and resets the index otherwise.
I assume that clicking on the "next button" calls the btnright() function.
If that is the case then you are testing the wrong value for undefined. You could rewrite your function as:
function btnright(){
load = imagenumber += 1;
// Test the value at the index of the array, not your index variable.
if (imagelist[load] === undefined) {
load = imagenumber-= 1;
}
document.getElementById('lightboxcontent').innerHTML = imagelist[load];
}
Stylistically this is still no the best. Your load variable is not required since its value always duplicates imagenumber. You could refactor the function such:
function btnright() {
// If we have a new array value do something.
if (imagelist[imagenumber + 1] !== undefined) {
// Increment the index and load the new image.
document.getElementById('lightboxcontent').innerHTML = imagelist[++imagenumber];
}
}
function btnleft() {
// If we're not on the first image do something.
if (imagenumber !== 0) {
// Decrement the index and load the new image.
document.getElementById('lightboxcontent').innerHTML = imagelist[--imagenumber];
}
}