Better way for al lot of if else - javascript

I have a function to change the background color depending on the value of a slider
There are 35 different colors and I now use this code for it (of course it is longer)
if (value < 25) {
color = '#FFFFFF';
} else if (value > 25 && value < 50) {
color = '#F8F8F8';
} else if (value > 50 && value < 75) {
color = '#F0F0F0 ';
}
Is there a way to shorten this up?

If you're incrementing by 25, then make an Array of colors:
var colors = ['#FFFFFF', '#F8F8F8', '#F0F0F0 ', ... ]
And then do a little math to see which index to use.
color = colors[(value - (value % 25)) / 25];
Or if you prefer:
color = colors[Math.floor(value / 25)];

You could make it a two line statement, without arrays, by doing something similar to this:
var rgbvalue = 255-Math.floor(value/25);
var color = 'rgb('+rgbvalue+','+rgbvalue+','+rgbvalue+');';
Of course you would have to limit the value, so that the rgbvalue doesn't get smaller than 0, but I guess you can easily do that, if you know the possible values.
And if you want it to get dark faster, you can multiply the result of the Math.floor operation, like this:
var rgbvalue = 255-(Math.floor(value/25)*5);
And you have the advantage that you don't have to write a huge array of shades of gray.

More bullet-proof version (not fully -proof though)
var colors = ['#FFFFFF','#F8F8F8','#F0F0F0'];
/* this is not that necessary */
var value = input_value || default_input_value;
var color = colors[ Math.floor(value/25) ];

colors = {'#FFFFFF','#F8F8F8','#F0F0F0 '}
color=colors[(int)value/25];
You may need to adjust this depending on the range of value.

Ditch the && and cascade instead
if(values > 75){
//anything above 75 falls here
}
else if(value > 50){
//anything <= 75 but > 50 falls here
}
else if(values > 25){
//anything <= 50 but > 25 falls here
}
else {
//anything <= 25 falls here
}

You could use an array of objects that describe the color and the min and max of the range and then use a function to iterate through the array to find the color between the range.
function getColor(value) {
var colorRanges = [
{ color : '#FFFFFF', min : 0, max : 25 },
{ color : '#F8F8F8', min : 25, max : 50 },
{ color : '#F0F0F0', min : 50, max : 75 }
],
length = colorRanges.length;
while(length--) {
var colorRange = colorRanges[length];
if (value >= colorRange.min && value < colorRange.max) {
return colorRange.color;
}
}
// default color
return colorRanges[0].color;
}
With a little additional effort, you could expose a way to add new colors and ranges, have a default for the range interval, etc. If your colors and range interval are fixed however, this is probably overkill.

Related

How to write a function to exponentially increase a number to its max

I have a variable called turnRadius that comes from user input and can be between -1 and 1, 0 being default.
I then have to convert this number into its equivalent in a range of 275 and 0 and store in variable spriteHorizontalPosition.
The problem is, for reasons I cant expect the user to set turnRadius all the way to -1 or 1, so I want that when turnRadius reaches 0.65 or -0.65, to increase exponentially to its max/min so that user doesnt have to reach the full number with input.
I think I get the idea but I don't know how to write the function, can I have some help?
Below is what I had, but I'm aware is not exponential and when it reaches 0.65 the spriteHorizontalPosition is suddenly yanked to its max and looks awkward.
let turnRadius = user.input;
if (turnRadius <= -0.65) {
turnRadius = -1;
} else if (turnRadius >= 0.65 ) {
turnRadius = 1;
}
spriteHorizontalPosition = ((turnRadius * -1) + 1) * 137.5;
if ( spriteHorizontalPosition >= 275 ) {
spriteHorizontalPosition = 275;
}
else if ( spriteHorizontalPosition <= 0 ) {
spriteHorizontalPosition = 0;
}
playerSprite.transform.x = spriteHorizontalPosition;
How about a nice cubic curve to map between the realistically possible user input (-0.65..0.65) and the desired (/ virtual) user range (-1..1)?
The smooth curve cubicMap can be given as:
const A = 0.65;
const C = 1/A - A*A;
const cubicMap = (x) => {
if (x > 0.65) return 1;
if (x < -0.65) return -1;
return C*x + x*x*x;
}
As seen in this graph: Desmos Link, it maps user input between -0.65 and 0.65 smoothly to between -1 and 1.
Your code could look like this:
const turnRadius = cubicMap(user.input);
const spriteHorizontalPosition = 275 * (1 + turnRadius) / 2;
playerSprite.transform.x = spriteHorizontalPosition;
There's no need for the extra if statements, the value is already clamped by cubicMap.

Find index by breakpoints instead of multiple else if statements

I am trying to generate sizes based on a persons height. For the moment my program works but I would like to find a shorter way to get the size instead of using all these else if statements. I would like to loop through "breakpoints" to find the corresponding index.
This is my original code + what i had in mind.
const sizes = ['xxs', 'xxs or xs', 'xs', 'xs or s'] // Goes on for all sizes...
function generateSize(height) {
let size;
if (height < 142) {
size = sizes[0];
} else if (height >= 142 && height < 148) {
size = sizes[1];
} else if (height >= 148 && height < 154) {
size = sizes[2];
} else if (height >= 154 && height < 160) {
size = sizes[3]; // Goes on for all sizes...
} else {
size = 'larger...';
}
return size;
}
// Example of what I had in mind.
const heightBreakpoints = [142, 148, 154, 160];
function getByBreakpoints(breakpoints, height){ // Part where I am stuck.
let index;
// Loop through breakpoints...
return index;
}
const sizeIndex = (getByBreakpoints(heightBreakpoints, 158));
const s = sizes[sizeIndex];
I think you could simplify this greatly just by tweaking your starting data structure. What if we had an array of objects that tie together size and its breakpoint:
const sizeMap = [
{ maxHeight: 142, size: 'xxs' },
{ maxHeight: 148, size: 'xxs or xs' },
{ maxHeight: 154, size: 'xs' },
{ maxHeight: 160, size: 'xs or s' },
]
const getSize = height => sizeMap.find(item => height < item.maxHeight).size
console.log(getSize(143))
Array function find returns the first value that satifsies your condition. The precondition for this approach to work is to have your array object's heights in ascending order.
const sizes = ['xxs', 'xxs or xs', 'xs', 'xs or s', "even another one here"]
// Goes on for all sizes...
function generateSize(height){
let size;
let left = 142;
let right = 142;
// Initialize the variables for comparison
// Left and right comparison based on position of "&"
// This is just user-defined
for(var i = 0; i < sizes.length; i++){
if(height < right){
size = sizes[i];
return size;
}
else {
// add counter from here
// This takes us to the next item in the array
i += 1;
// add right comparison with intervals of 6
right += 6;
if(height >= left && height < right){
size = sizes[i];
return size;
}
else {
// add left comparison with intervals of 6
left += 6;
// revert the counter to its initial value
i -= 1;
}
}
}
}
console.log("First: " + generateSize(141))
console.log("Second: " + generateSize(147))
console.log("Third: " + generateSize(153))
console.log("Fourth: " + generateSize(159))
console.log("Last: " + generateSize(161));
// Note this 161, which will return the new last value in the array
This assumes your sizes are at intervals of 6, (which they are) and returns respective values corresponding to the array
if(height<160){
height-=142;
if(height<0){size=sizes[0]}
else{
size=sizes[(hieght)%6]
}
}
else{
size='larger...'
}
check if this works in all cases I am sleepy

JavaScript check if array contains modified values, count unique pixels in an image

var image = new SimpleImage("lena.png");
var col = [];
var uniqcol = [];
for (var px of image.values()){
col.push([px.getRed,px.getGreen,px.getBlue]);
if(uniqcol.includes([px.getRed +- 1, px.getGreen +- 1, px.getBlue +- 1]) ){
print('not unique');
}else{
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
}
}
I would like to count the number of unique pixels within an image. A unique pixel being one which RGB values are not within 1 to anothers pixels. I have the above code but it does not work. I think the issue that I am having is with checking that the RGB values are either +1 or -1 from the selected pixel px value. If a unique pixel is found, id like to add to the the uniqcol array. Is there any other way to count the unique pixels, or a way to check that the RGB values are within 1 from the selected px value?
Thanks.
This tests each component to see if it's within 1 by subtracting the two, taking the absolute value, and checking if it's less than 2.
This is probably super inefficient. For each pixel you're iterating a potentially massive array until you get a match, or worst case, you don't find a match.
var image = new SimpleImage("lena.png");
var col = [];
var uniqcol = [];
for (var px of image.values()){
var found = uniqcol.find(function (el) {
return
Math.abs(el[0] - px.getRed) < 2 &&
Math.abs(el[1] - px.getGreen) < 2 &&
Math.abs(el[2] - px.getBlue) < 2;
});
if (!found) {
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
} else {
print('not unique');
}
}
Here's another approach that uses memoization. It should be a lot faster at the expense of storing a separate lookup structure.
Edit - I deleted this approach because it can fail. It's probably possible to do but quite tricky.
You need to check for all the different pixel values, putting +- will not match a range of values. .includes() looks for exact matches.
for (var px of image.values()) {
col.push([px.getRed,px.getGreen,px.getBlue]);
var found = false;
for (dRed of [-1, 0, +1]) {
for (dGreen of [-1, 0, +1]) {
for (dBlue of [-1, 0, +1]) {
if (uniqcol.includes([px.getRed + dRed, px.getGreen + dGreen, px.getBlue + dBlue]) {
found = true;
print("not unique");
break;
}
}
if (found) {
break;
}
if (found) {
break;
}
}
if (!found) {
uniqcol.push([px.getRed,px.getGreen,px.getBlue]);
}
}
This is probably not a very efficient way to do it, since it will search the entire image 9 times for each pixel. It would probably be better to loop through all the pixels, testing if all the colors are within a range of the current pixel:
if (px.getRed >= curPixel.getRed - 1 && px.getRed <= curPixel.getRed + 1 &&
px.getGreen >= curPixel.getGreen - 1 && px.getGreen <= curPixel.getGreen + 1 &&
px.getBlue >= curPixel.getBlue - 1 && px.getBlue <= curPixel.getBlue + 1)
A really efficient algorithm would involve sorting all the pixels (nested arrays of red, blue, and green values would be a good structure), then searching this. But that's more a topic for CodeReview.stackexchange.com.

Javascript and Variable assistance

(Rephrasing question from earlier) So here is the assignment:
First, you will have to calculate a cost for the weight of the parcel. The user will enter the total weight of their parcel into the text field. The schedule is as follows…
0 – 150 lbs $20.00 per pound
| 151 – 300 lbs $15.00 per pound
| 301 – 400 lbs $10.00 per pound
Do not allow the user to enter a weight that is < 0 or > 400. If they do, output an error message in red to div#results and ‘return’ out of the function.
Next, the user will choose a discount amount (for whatever reason, does not matter). You will need to apply whatever discount amount is chosen. (50% off, 20% off, none).
This is what I have done so far. Variable aren't declared yet, just wrote them in.
function calcTotal() {
var msg;
var weight = parseInt( document.getElementById("weight").value );
var discount;
var total;
if( weight >= 0 && weight <= 150 ) {
total = weight * 20
}
else if( weight >150 && weight <= 300 ) {
total = weight * 15
}
else if( weight >300 && weight <= 400 ) {
total = weight * 10
}
if( document.getElementById("50%").selected == true ) {
total = total * 0.50;
}
if( document.getElementById("25%").selected == true ) {
total = total * 0.25;
}
if( document.getElementById("none").selected == true ) {
total = total;
}
Is this somewhat correct so far?
Can't seem to figure out how to apply the discount based on what the user selects. The discounts are 3 radio buttons. Do i need to apply an id to each radio button?
First of all you need to use && (AND) instead of || (OR) because you want both conditions to be met not just one. First IF statement will process value -1000 as TRUE (as well as any other value because your interval is from 0 to infinity plus from minus infinity to 150) because it satisfies the second part of the first condition.
Second, the formula is correct but you have to convert percents into 0-1 interval. 100% = 1, 0% = 0 and x% = x/100. Then it should work without any problems.
Last thing to do is that you need to pass the values into your function:
function calcTotal(weight, discount) {
// do the calculation with passed values, you do not need to declare them here anymore
}
Or you need to set values right inside of that function, e.g.:
function calcTotal() {
var discount = $("#inputField").val(); // using jQuery, where inputField is element
// from which the value is taken e.g. < input >
...
}
To display the final output, add this to your function:
$("body").append("<div id='output'>" + output + "</div>"); // using jQuery, watch for single/double quotes
and style it with css to be in the center:
#output {
position: absolute;
width: 200px;
height: 200px;
left: 50%;
margin-left: -100px;
top: 200px;
}
I made a fiddle that you should be able to get what is going on pretty quickly.
http://jsfiddle.net/a58rR/3/
I used a touch of jQuery just to get the bindings on the UI elements.
I hope this is not for a class and you copy this wholesale! ;)
Basically, I put all your Tier pricing into one object.
Tiers = [{
nPrice: 20,
nWeightMin: 1,
nWeightMax: 150
}, {
nPrice: 15,
nWeightMin: 151,
nWeightMax: 300
}, {
nPrice: 10,
nWeightMin: 301,
nWeightMax: 400
}];
Then, your function will calculate based on the entered weight and discount selected, determine the Tier, validate the weight, update the UI with a message if out of range, calculate the final price and apply any discount, and update the UI with the total price:
function calculatePrice() {
console.log('Begin Calc');
var _nW = document.getElementById('nParcelWeight').value * 1;
var _nD = document.getElementById('aDiscounts').value * 1;
var _nP = 0;
var nTotalPrice = 0;
var _TotalPrice = document.getElementById('nPrice');
var _nMaxWt = Tiers[Tiers.length - 1].nWeightMax;
// Using the last Tier keeps the max weight dynamic no matter how many tiers you add as long as they are in order
console.log('Max Weight: ' + _nMaxWt);
console.log('Weight: ' + _nW);
console.log('Discount: ' + _nD);
if (isNaN(_nW) || _nW < 1 || _nW > _nMaxWt) {
// Throw/Display an out of range error here
console.log('Yep, out of range');
document.getElementById('uiFeedback').innerHTML = 'The number is out of range.';
} else {
// reset if valid
document.getElementById('uiFeedback').innerHTML = '';
}
// Find Tier
for (var i = 0; i < Tiers.length; i++) {
console.log('we are in loop:' + i);
if (_nW >= Tiers[i].nWeightMin && _nW <= Tiers[i].nWeightMax) {
_nP = Tiers[i].nPrice;
break;
}
}
console.log('Tier: ' + i);
console.log('Price: ' + _nP);
// Calculate Discount
if (_nD != 1) _nD = 1 - _nD; // (20%==.20, but that would be .80 of the Price, etc)
// Calc Price
nTotalPrice = (_nP * _nW * _nD);
_TotalPrice.value = nTotalPrice;
}
The html will look something like this:
<div id='uiFeedback'></div>Parcel Weight:
<input id='nParcelWeight' value='0'>Discount:
<select id='aDiscounts'>
<option value='1'>none</option>
<option value='.2'>20%</option>
<option value='.5'>50%</option>
</select>
<hr>Price:
<input id='nPrice'>
And your CSS at a minimum might just color your messaging:
#uiFeedback {
color: red;
font-weight: bold;
}
Here are the bindings, which you could do with inline onChanges or raw js attach events:
$(function () {
$('#nParcelWeight,#aDiscounts ').on('change', function () {
calculatePrice();
});
})

Finding 3 highest numbers in html data table and marking them red

Problem description:
I have data coming in that shows the electricity consumption of various buildings. These are in the form of html tables and may, and probably will, contain duplicates.
Using jQuery, how do I pick out the 3 highest numbers and mark them by making the background red?
Note: it's the three individually highest numbers that I want highlighted/marked red.
So if the in-data for example gives me 190, 180, 180, 170, 150 etc. I would like to highlight 190, 180 IN ONE PLACE/CELL, and 170.
Also, I wish to treat the tables as if I have only read-rights, not changing the table in any way except for the highlighting of the three highest numbers.
Psuedo code:
var high = [],
done = 0,
final = [];
$("td").each(function(){
high.push({ cell : $(this), val : parseInt($(this).text())});
});
// Now we got all cells sorted DESC
high.sort(function(a, b){
return b.val - a.val;
});
for(var i=0;i<high.length && done < 3;i++){
// Max highest 3
if( $.inArray(high[i].val, final) == -1 ) {
final.push(high[i].val);
done++;
}
high[i].cell.addClass("red");
}
Alternative psuedo code
var final = [],
done = 0,
items = $("td").sort(function(a, b){
return parseInt($(b).text()) - parseInt($(a).val());
});
items.each(function(){
var val = parseInt($(this).val());
if( $.inArray(val), final) == -1 ) {
done++;
final.push(val);
}
if( done == 3 ) {
return false; // Stop after we got enough
}
$(this).addClass("red");
});

Categories