I'm writing a program in JS and im feeling i'm repeating code, which is not good. I'm trying to avoid an if then else block that has two similar for loop and re-write it without an if then else using just one for loop.
Consider this: minimum has value 0. maximum has value 10. if new_value is less than old_value i wanna execute a for loop from minimum to new_value, else i wanna execute it from maximum DOWNto new_value
Lets see it in action, lets say javascript (language-agnostic answers are welcome and upvoted -but will not grant you an extra cookie)
var minimum = 0;
var maximum = 10;
var old_value = 5;
/* var new_value = taken from user input whatever ... */
if(new_value<old_value)
{
for(i=minimum;i<new_value;i++)
{
// whatever
}
}
else
{
for(i=maximum;i>new_value;i--)
{
// whatever
}
}
I have a feeling these two for loops are similar enough to be written as one in a mathematical approach maybe. Have tried a bit using absolute values Math.abs() Math.max.apply() but had no luck.
I don't want to set other helping variables using if then else to give appropriate values.
So, whats the question: I'm wondering if this can be rewritten in one for ... loop without being nested in an if then else.
A complete solution using built-in js functions will grant you an extra cookie.
Edit: Didn't see your original thing about not using the if/else with variables. Why not do something like this then? Just go from 0 to 10, using that value or 10 minus that value depending on the conditional.
for(var j = 0; j <= 10; j++) {
var i = new_value < old_value ? j : 10 - j;
// whatever
}
Assign your min, max and increment as variables, define them based on your if condition and then use them in the for loop:
var old_value = 5, start, end, inc;
if(new_value<old_value) {
start = 0;
end = 10;
inc = 1;
} else {
start = 10;
end = 0;
inc = -1;
}
for( i = start;i >= start && i <= end; i += inc) {
// whatever
}
You could abuse of the ternary operator just for fun:
var minimum = 0;
var maximum = 10;
var old_value = 5;
var new_value = 7;
/* var new_value = taken from user input whatever ... */
var check =(new_value<old_value);
var foo1 = function () { console.log("foo1") }
var foo2 = function () { console.log("foo2") }
for(i=check?minimum:maximum;
check?(i<new_value):(i>new_value);
check?i++:i--)
{
check?foo1():foo2();
}
Does the second loop have to iterate in reverse? If not you can simply use
var i0 = new_value<old_value ? minimum : new_value+1;
var i1 = new_value<old_value ? new_value : maximum+1;
for(i=i0;i<i1;++i)
{
//whatever
}
Edit: In the light of your comment, if you can be sure that you're dealing with integers you can use
var i0 = new_value<old_value ? minimum : maximum;
var d = new_value<old_value ? 1 : -1;
for(i=i0;i!=new_value;i+=d)
{
//whatever
}
If not
var i0 = new_value<old_value ? minimum : maximum;
var d = new_value<old_value ? 1 : -1;
for(i=i0;d*i<d*new_value;i+=d)
{
//whatever
}
I did it!
With this +(old_value>new_value) instead of getting true/false i am getting 1/0. I am using the 1 and 0 as multipliers to emulate the if then else functionality.
lets assume
var minimum = 0;
var maximum = 10;
var old_value = 5;
for(expression1;expression2;expression3)
For expression1:
if new value is bigger than old value then we need minimum
if new value is smaller than old value then we need maximum
I am multiplying be zero the maximum depending the above conditions with (maximum*(+(old_value>new_value)))
I am multiplying by zero the minimum depending the above conditions with (minimum*(+(old_value<new_value))
by adding these two the sum is what i am supposed to get! (maximum*(+(old_value>new_value)))+(minimum*(+(old_value<new_value)))
This will give minimum if new_value > old value and maximum if new_value < old_value
For expression2:
while i!=new_value; simple. (we just have to be sure the maximum is bigger than new_value and minimum is smaller than new_value or we have an endless loop.)
For expression3:
if new value is bigger than old value then we need i=i +1
if new value is smaller than old value then we need i=i -1
this
(+(old_value<new_value)+1)+(-1*(+(old_value>new_value)+1))
will give either 2+-1=1 or 1+(-2)=-1 so we simply use it in expression3 as
i=i+(+(old_value<new_value)+1)+(-1*(+(old_value>new_value)+1))
complete code:
http://jsfiddle.net/eBLat/
var minimum = 0;
var maximum = 10;
var old_value = 5;
var new_value = 3; // change this value
// if new value is bigger than old value then for loop from maximum downto new_value (dont include new value)
// if new value is smaller than old value then for loop from minimum upto new_value (dont include new value)
for(i=(maximum*(+(old_value>new_value)))+(minimum*(+(old_value<new_value)));i!=new_value;i=i+(+(old_value<new_value)+1)+(-1*(+(old_value>new_value)+1)) )
{
alert("Iteration:"+i);
}
Another question would be if this is actually better than just write two for in a if then else ... anyway i had fun. And i got the cookie :D :D
Hope someone will find useful in some way the fact that +true gives 1 and +false gives 0 in javascript
Related
I am currently trying to make a program where the text changes as the phone moves every couple value(s) using the P5.JS deviceMoved() function.
(the gif below displays how i wanted the text to change eventually as the device moved)
As seen on the code below, I've put all the text in the array and I wanted to change the index to +1 each time say the move value ads 30 and repeat until all the text is gone.
let button;
let permissionGranted = false;
let nonios13device = false;
let cx, cy
let value = 0;
var myMessages = ["The", "Quick", "Brown", "Fox", "Jumped", "Over", "The", "Lazy", "Dog"];
var index = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
background(255)
text(myMessages[index], width / 2, height / 2);
fill(value);
text(value, width / 3, height / 3);
textSize(30)
}
function deviceMoved() {
value = value + 5;
if (value > 255) {
value = 0;
}
}
function onMove() {
var currentValue = value + 30;
if (value = currentValue) {
index++;
return;
}
if (index >= myMessages.length) {
index = 0;
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.3.1/lib/p5.js"></script>
I think my problem is within the onMove function, where I need to define the current value and what values could change the text, I'm fairly new at this so any insight/solution to do this would be highly appreciated :)
Thank you!
There are several issues related to the onMove function. First and foremost it is never called, and unlike deviceMoved it is not a special function that p5.js automatically invokes. Additional issues:
function onMove() {
// You create a currentValue variable that is just value + 30.
// Within the same function, checking if value is >= currentValue,
// assuming that is what you intended, will be fruitless because it
// is never true.
// What you probably want to do is declare "currentValue" as a global
// variable and check the difference between value and currentValue.
var currentValue = value + 30;
// This is the assignment operator (single equal sign), I think you meant
// to check for equality, or more likely greater than or equal to.
if (value = currentValue) {
index++;
// You definitely do not want to return immediately here. This is where
// you need to check for the case where index is greater than or equal
// to myMessages.length
return;
}
if (index >= myMessages.length) {
index = 0;
}
}
Here's a fixed version:
function deviceMoved() {
value = value + 5;
if (value > 255) {
// When value wraps around we need to update currentValue as well to
// keep track of the relative change.
currentValue = 255 - value;
value = 0;
}
onMove();
}
let currentValue = 0;
function onMove() {
if (value - currentValue >= 30) {
// Update currentValue so that we will wait until another increment of
// 30 before making the next change.
currentValue = value;
index++;
// We only need to make this check after we've incremented index.
if (index >= myMessages.length) {
index = 0;
}
}
}
In order to test this out on my mobile device (iOS 14) I had to add some code to request access to the DeviceMotionEvent, and host it in an environment using HTTPS and not embedding in an iframe. You can see my code on glitch and run it live here.
I'm writing analytics and I have to initialize counter counts for (keys) hours, days, weeks, years so as to get frequency of user activity. I need to create a hit count for respective time and increment accordingly. Visits are fed via a loop.
I have this working but I'm not sure if the code below is ideal to do so.
if(!analytics.users[message.user].counts.hourly[hour]) {
analytics.users[message.user].counts.hourly[hour] = 0;
}
analytics.users[message.user].counts.hourly[hour] += 1;
if(!analytics.users[message.user].counts.daily[day]) {
analytics.users[message.user].counts.daily[day] = 0;
}
analytics.users[message.user].counts.daily[day] += 1;
...
I've tried the x = x + 1 || 0 method but that hasn't worked.
Also, is there a way I can set up a function for this?
You could use a function which take the object and the key and perfoms the check and update.
function increment(object, key) {
if (!object[key]) object[key] = 0;
++object[key];
}
Call with
increment(analytics.users[message.user].counts.hourly, hour);
I've tried the x = x + 1 || 0
You almost got it. It should either be:
x = x || 0;
x++;
Or
x = x + 1 || 1;
So, change your code to:
analytics.users[message.user].counts.hourly[hour] =
(analytics.users[message.user].counts.hourly[hour] + 1) || 1
If analytics.users[message.user].counts.hourly[hour] is undefined, the increment operation returns NaN. This is a falsy value. So, it takes 1
You can create a simple increment function like the one below. It first checks for the key to be initialized and if not, it will initialize it to 0. The next line with the increment is safe to execute since the key was previously created.
let message = {
user: "user"
}
let analytics = {
users: {
"user": {
counts: {
}
}
}
}
function incrementAnalytics(analytics, period) {
analytics[period] = analytics[period] || 0;
++analytics[period];
}
let test = analytics.users[message.user].counts;
incrementAnalytics(test, "hourly");
incrementAnalytics(test, "hourly");
incrementAnalytics(test, "daily");
console.log(test);
Cheers!
So, I have successfully written the Fibonacci sequence to create an array with the sequence of numbers, but I need to know the length (how many digits) the 500th number has.
I've tried the below code, but its finding the length of the scientific notation (22 digits), not the proper 105 it should be returning.
Any ideas how to convert a scientific notation number into an actual integer?
var fiblength = function fiblength(nth) {
var temparr = [0,1];
for(var i = 2; i<=nth; i++){
var prev = temparr[temparr.length-2],
cur = temparr[temparr.length-1],
next = prev + cur;
temparr.push(next);
}
var final = temparr[temparr.length-1].toString().length;
console.log(temparr[temparr.length-1]);
return final;
};
a = fiblength(500);
console.log(a);
Why not use the simple procedure of dividing the number by 10 until the number is less than 1.
Something as simple as this should work (a recursive def obv works as well)
function getDigits(n) {
var digits = 0;
while(n >= 1) {
n/=10;
digits += 1;
}
return digits;
}
getDigits(200);//3
getDigits(3.2 * 10e20);//=>22
Here's a solution in constant time:
function fiblength(n) {
return Math.floor((n>1)?n*.2089+.65051:1);
}
Let's explain how I arrived to it.
All previous solutions will probably not work for N>300 unless you have a BigNumber library in place. Also they're pretty inneficient.
There is a formula to get any Fibonacci number, which uses PHI (golden ratio number), it's very simple:
F(n) = ABS((PHI^n)/sqrt(5))
Where PHI=1.61803399 (golden ratio, found all over the fibonacci sequence)
If you want to know how many digits a number has, you calculate the log base 10 and add 1 to that. Let's call that function D(n) = log10(n) + 1
So what you want fiblength to be is in just the following function
fiblength(n) = D(F(n)) // number of digits of a fibonacci number...
Let's work it out, so you see what the one liner code will be like once you use math.
Substitute F(n)
fiblength(n) = D(ABS((PHI^n)/sqrt(5)))
Now apply D(n) on that:
fiblength(n) = log10(ABS((PHI^n)/sqrt(5))) + 1
So, since log(a/b) = log(a) - log(b)
fiblength(n) = log10(ABS((PHI^n))) - log10(sqrt(5))) + 1
and since log(a^n) = n * log(a)
fiblength(n) = n*log10(PHI) - log10(sqrt(5))) + 1
Then we evaluate those logarithms since they're all on constants
and add the special cases of n=0 and n=1 to return 1
function fiblength(n) {
return Math.floor((n>1)?n*.2089+.65051:1);
}
Enjoy :)
fiblength(500) => 105 //no iterations necessary.
Most of the javascript implementations, internally use 64 bit numbers. So, if the number we are trying to represent is very big, it uses scientific notation to represent those numbers. So, there is no pure "javascript numbers" based solution for this. You may have to look for other BigNum libraries.
As far as your code is concerned, you want only the 500th number, so you don't have to store the entire array of numbers in memory, just previous and current numbers are enough.
function fiblength(nth) {
var previous = 0, current = 1, temp;
for(var i = 2; i<=nth; i++){
temp = current;
current = previous + current;
previous = temp;
}
return current;
};
My Final Solution
function fiblength(nth) {
var a = 0, b = 1, c;
for(var i=2;i<=nth;i++){
c=b;
b=a+b;
a=c;
}
return Math.floor(Math.log(b)/Math.log(10))+1;
}
console.log(fiblength(500));
Thanks for the help!!!
The problem is because the resulting number was converted into a string before any meaningful calculations could be made. Here's how it could have been solved in the original code:
var fiblength = function fiblength(nth) {
var temparr = [0,1];
for(var i = 2; i<=nth; i++){
var prev = temparr[temparr.length-2],
cur = temparr[temparr.length-1],
next = prev + cur;
temparr.push(next);
}
var x = temparr[temparr.length-1];
console.log(x);
var length = 1;
while (x > 1) {
length = length + 1;
x = x/10;
}
return length;
};
console.log ( fiblength(500) );
Math.floor((1600 * Math.pow(1.4, 19))); // = 956208
Im doing a fansite for a game and im trying to make a calculation how how much mana you need to get + skill the formula above calculates from 19-20 skill
but i need to loop the calculation x amount of times so you can calculate from x (19 in the calculation above) to y need to raise x+1 each time until it reaches the final value y and add up the answeres from each loop like below
i have 2 text boxes that i take the values from
956208+1338692+1874168+2623836+3673371+5142719 = 15608994 so it would end up doing something like that thats from 19 to 25
If I am understanding the problem correctly (it's a bit unclear...) you want something like this.
var from = 19;
var to = 25;
var totalMana = 0;
for (var i = from; i <= to; i++) {
totalMana += Math.floor(1600 * Math.pow(1.4, i));
}
console.log(totalMana); // 22,808,801
You simply loop from your lower value, through to your upper value, evaluating your formula each time and adding it to a accumulation variable that persists through each iteration.
Also, just so you know, you are dipping your toe in calculus. You are getting the summation of a finite series. Math is fun, even if you dont know your doing it :)
Gonna throw this answer in for fun:
// generate an array with each value in the series, where callback evaluates
// the value at each step
Math.series = function(from, to, callback){
var out = [];
for (var i = from; i <= to; i++)
out.push(callback.call(null, i));
return out;
};
// add the things in an array.
Math.sum = function(arr){
var sum = 0;
for (var i = 0; i < arr.length; i++)
sum += +arr[i];
return sum;
};
With these utility functions, you can accomplish your task in a one-liner like so:
Math.sum(Math.series(19,25,function(i){return Math.floor(1600*Math.pow(1.4,i));}));
I am trying to make a script to pick random number between two numbers . but it picks same number sometimes. i donot want to repeat same number until array is finished .
Here is my code
$(document).ready(function () {
abc();
test = array();
function abc() {
res = randomXToY(1, 10, 0);
$('#img' + res).fadeTo(1200, 1);
//$(this).addClass('activeImg');
//});
setTimeout(function () {
removeClassImg(res)
}, 3000);
}
function removeClassImg(res) {
$('#img' + res).fadeTo(1200, 0.1);
//$('#img' + res).removeClass('activeImg');
abc();
}
function randomXToY(minVal, maxVal, floatVal) {
var randVal = minVal + (Math.random() * (maxVal - minVal));
return typeof floatVal == 'undefined' ? Math.round(randVal) : randVal.toFixed(floatVal);
}
});
Does Anybody have idea about this ...
You'll have to maintain a list of numbers that have already been generated, and check against this list. Re-generate a new number if you find a dupe.
If you do not want the random numbers repeating themselves you have to keep track of the some way.
If you have the range you are dealing with is relatively small, you can create an array with all possible results and simply randomly pick out of it.
function Randomizer(minVal, maxVal, floatVal){
var possible_results = []; // for larger arrays you can build this using a loop of course
var randomization_array = [];
var count = minVal;
var incrementor = floatVal || 1; // set the distance between possible values (if floatVal equals 0 we round to 1)
while (count <= maxVal) {
possible_results.push(count);
count += incrementor;
}
this.run = function(){
// if randomization_array is empty set posssible results into it
randomization_array = randomization_array.length ? randomization_array : $.merge(randomization_array, possible_results);
// pick a random element within the array
var rand = Math.floor(Math.random()*randomization_array.length);
// return the relevant element
return randomization_array.splice(rand,1)[0];
}
}
and in order to use it (it creates a specialized object for each possible range):
rand = new Randomizer(1,10,0);
rand.run();
note that this approach does not work well for very large ranges