.toFixed() Number If Too Big For Element - javascript

There is a unique number displayed to users of particular groups on their account dashboard. This number is fetched from the database and changed frequently. The number is less than 10,000,000.
What would be the best way to change the number from let's say "2,643,977" to the likes of "2.6M" if the width of the element starts to cut off the text, so lets say 100px can show 2,643,977, but reduced to 50px it shows 2.6M?
Question Clarification
The text element is a percentage, therefore smaller screens will cut of large numbers.
Is there a way obtain the inner elements text size?
On this inner text width being greater than element width, shorten the number, be it 1,000 - 1k, 1,000,000 - 1m...

You could use a js function like the following to check if your content is too wide, then use one of the functions provided in the other answers to shorten it if so.
This function accepts the text you want to check, the width you want to constrain it to in px, and the css classes your actual element uses (in a space-separated string). It returns a bool indicating whether the text fits.
function checkIfFits(text, width, classes) {
var s = document.createElement("span");
s.className = classes;
s.innerHTML = text;
if (parseInt(s.offsetWidth, 10) > width) return false;
return true;
}

How about this:
n2Display = (n<1000000) ? n : (n/1000000).toFixed(1) + 'M';
I'm assuming that n is a number, not a string here

function shorten(n){
var l=Math.floor(n.length/3-1);
return n.slice(0,n.length-l*3)+["","k","M","B"][l];
}
alert(shorten("123456"));
alert(shorten("123456789123"));
Note that this requires a String from Int to be passed.
It simply cuts off every block of 3 numbers and replaces it with its shortform:
123,456 => 123k
123,456,789 => 123M

Related

Finding a pattern in an array that is not always consistant

I have an ordered data set of decimal numbers. This data is always similar - but not always the same. The expected data is a few, 0 - 5 large numbers, followed by several (10 - 90) average numbers then follow by smaller numbers. There are cases where a large number may be mixed into the average numbers' See the following arrays.
let expectedData = [35.267,9.267,9.332,9.186,9.220,9.141,9.107,9.114,9.098,9.181,9.220,4.012,0.132];
let expectedData = [35.267,32.267,9.267,9.332,9.186,9.220,9.141,9.107,30.267,9.114,9.098,9.181,9.220,4.012,0.132];
I am trying to analyze the data by getting the average without high numbers on front and low numbers on back. The middle high/low are fine to keep in the average. I have a partial solution below. Right now I am sort of brute forcing it but the solution isn't perfect. On smaller datasets the first average calculation is influenced by the large number.
My question is: Is there a way to handle this type of problem, which is identifying patterns in an array of numbers?
My algorithm is:
Get an average of the array
Calculate an above/below average value
Remove front (n) elements that are above average
remove end elements that are below average
Recalculate average
In JavaScript I have: (this is partial leaving out below average)
let total= expectedData.reduce((rt,cur)=> {return rt+cur;}, 0);
let avg = total/expectedData.length;
let aboveAvg = avg*0.1+avg;
let remove = -1;
for(let k=0;k<expectedData.length;k++) {
if(expectedData[k] > aboveAvg) {
remove=k;
} else {
if(k==0) {
remove = -1;//no need to remove
}
//break because we don't want large values from middle removed.
break;
}
}
if(remove >= 0 ) {
//remove front above average
expectedData.splice(0,remove+1);
}
//remove belows
//recalculate average
I believe you are looking for some outlier detection Algorithm. There are already a bunch of questions related to this on Stack overflow.
However, each outlier detection algorithm has its own merits.
Here are a few of them
https://mathworld.wolfram.com/Outlier.html
High outliers are anything beyond the 3rd quartile + 1.5 * the inter-quartile range (IQR)
Low outliers are anything beneath the 1st quartile - 1.5 * IQR
Grubbs's test
You can check how it works for your expectations here
Apart from these 2, the is a comparison calculator here . You can visit this to use other Algorithms per your need.
I would have tried to get a sliding window coupled with an hysteresis / band filter in order to detect the high value peaks, first.
Then, when your sliding windows advance, you can add the previous first value (which is now the last of analyzed values) to the global sum, and add 1 to the number of total values.
When you encounter a peak (=something that causes the hysteresis to move or overflow the band filter), you either remove the values (may be costly), or better, you set the value to NaN so you can safely ignore it.
You should keep computing a sliding average within your sliding window in order to be able to auto-correct the hysteresis/band filter, so it will reject only the start values of a peak (the end values are the start values of the next one), but once values are stabilized to a new level, values will be kept again.
The size of the sliding window will set how much consecutive "stable" values are needed to be kept, or in other words how much UNstable values are rejected when you reach a new level.
For that, you can check the mode of the values (rounded) and then take all the numbers in a certain range around the mode. That range can be taken from the data itself, for example by taking the 10% of the max - min value. That helps you to filter your data. You can select the percent that fits your needs. Something like this:
let expectedData = [35.267,9.267,9.332,9.186,9.220,9.141,9.107,9.114,9.098,9.181,9.220,4.012,0.132];
expectedData.sort((a, b) => a - b);
/// Get the range of the data
const RANGE = expectedData[ expectedData.length - 1 ] - expectedData[0];
const WINDOW = 0.1; /// Window of selection 10% from left and right
/// Frequency of each number
let dist = expectedData.reduce((acc, e) => (acc[ Math.floor(e) ] = (acc[ Math.floor(e) ] || 0) + 1, acc), {});
let mode = +Object.entries(dist).sort((a, b) => b[1] - a[1])[0][0];
let newData = expectedData.filter(e => mode - RANGE * WINDOW <= e && e <= mode + RANGE * WINDOW);
console.log(newData);

Function loop comparing three variables to three values contained in a nested array to find best match

I have been a lurker on this site for a while while I have been designing a small program in JavaScript. I have run into an issue that I cant seem to solve though, and I need your help!
You can find my question at the bottom of the post. in layman's terms, I need help setting up a function loop to compare three values against three values in a nested array, where each value can be compared to all three values in the array.
I am working on a type of calculator to find the best way to cut a 3D rectangular piece of material to get the best yield, given a certain cut size I need. I have many different potential sizes of material to cut from, sorted in a nested array, like this:
data[i][x] where i = number key of material arrays available, and x(each array) is set like so
[material ID#, height, width, length, volume]
What I have is a cut size i need to cut out of the parent material, which consists of: height, width, length.
The problem I have run into is the best way to align the cut size in the parent material (comparing each value against the other: height, width, length) to return the highest yield possible.
I will mention that I do have a few constraints.
First, whatever lines up in the height measurement in the parent material can only be 1 cut size tall. The other two dimensions (length and width) can be multiple sizes each if they fit.
so, to reiterate constraints:
Height must be 1 unit.
Width can be multiple units
Length can be multiple units
The orientation of the cut piece does not matter, so long as these constraints are held to.
Length or width can be a decimal, but only whole cut sizes will count.
Below, I have placed some code I designed to do this, but the problem is it is not nearly dynamic enough, as you can see. it only compares height to height, width to width, and so forth.
The input for this is as follows:
cutheight;
cutwidth;
cutlength;
data[i][x]; // each nested data array is like so: [material ID key, height of material, width of material, length of material, volume of material]
for (i = 0; i < data.length; i++) {
if((data[i][1] > cutheight) && (data[i][2] > cutwidth) && (data[i][3] > cutlength)) {
insertSort(results, data[i], (Math.min((data[i][4] * (Math.ceil((numCutPiecesNeeded) / ((Math.floor(data[i][2]/cutwidth)) * (Math.floor(data[i][3]/cutlength)))) ) ))) ); // the last step in this function determines pieces yielded per material
}
}
function insertSort(array, match, materialVolumeMin){
//itterate through the results and see where the new match should be inserted
for( j = 0; j < array.length; j++){
// if the billetVolumeMin of match is less than the materialVolumeMin of the item at position j
if(array[j][5] > materialVolumeMin){
//insert the match at position j
array.splice(j, 0, match);
array[j].push(materialVolumeMin);
return true;
}
}
// if the materialVolumeMin of match is not less than anything in array then push it to the end. This should also cover the case where this is the first match.
// push match
match.push(materialVolumeMin);
array.push(match);
// set match index 5 to the materialVolumeMin for future comparison
//array[array.length -1].push(materialVolumeMin);
return true;
}
What this code does is return all the nested arrays with a new value attached to the end, which is in effect the total volume of material needed to get the cuts you need out of it. I use this volume as a way of sorting the arrays to find the most cost effective (or highest yield) material to use.
I believe this is more of a logic question, but I have no idea how to do this. I do know that it would be better to have multiple cuts available on the height axis as well, but due to various factors this is not possible.
I also believe that the height should be found first, by comparing all three dimensions of the cut size to the parent material, and finding the one that has the least waste
How I see this maybe happening:
Math.min((data[i][1]-cutheight), (data[i][1]-cutwidth), (data[i][1]-cutlength));
From there, I really dont know. I appreciate any and all help, advice, or suggestions. Thank you!
Edit: I dont think I was clear in my Question. The code above is how im going about the problem now, but is by no means how I want to actually do it in the finished program.
My question is:
How can I set up a function to compare my three sizes to each possible material size in my nested array, and return the best match, where data[i][5] (which is data[i][4] (volume of material) times the number of cut pieces I need, divided by how many fit in said parent material) is the smallest out of all of my possible choices.

Formatting Interpolated String or Number in d3

I'm using d3 to animate text to show a user's progress towards completing a task. For example, if they've completed 32.51% of the task, the text will animate from 0% to 32.51% over 2 seconds or so.
To do this, I'm using d3's attrTween method on an svg text element in conjunction with d3.interpolate. The interpolation is working great, but I'm having a little trouble formatting the text. I'd like the text to always display 4 digits, so 0% = 00.00%, 4.31% = 04.31% etc. It would be nice to be able to do this without necessarily having to post process what the interpolator returns. In other words, without having to take the returned percentage and check to see if there are 4 digits and add zero padding on either side before placing it in the DOM.
As a test, I tried specifying the format that I would like by setting the a and b values to the interpolator like so d3.interpolate("00.00", "30.00"), but the final text is "30" with the trailing zeros cut off.
Any suggestions?
You can add a custom interpolator to d3.interpolators - see the docs. The example given in the docs is very close to yours - the only real change is specifying the output format, which in your case should be:
d3.format('05.2f'); // 0-padding, string width 5, 2 decimal places
Plugging this into the doc example (note that I also changed the regex appropriately and added the percentage sign):
d3.interpolators.push(function(a, b) {
var re = /^(\d\d\.\d\d)%$/, ma, mb, f = d3.format('05.2f');
if ((ma = re.exec(a)) && (mb = re.exec(b))) {
a = parseFloat(ma[1]);
b = parseFloat(mb[1]) - a;
return function(t) {
return f(a + b * t) + '%';
};
}
});
d3.interpolate("00.00%", "30.00%")(1/5); // "06.00%"
d3.interpolate("00.00%", "30.00%")(1/3); // "10.00%"

How to increase a CSS value by the value of a JS variable?

I have several fixed position divs with the same class at varying distances from the left edge of the window, and I'd like to increase/decrease that distance by an equal amount on each div when a certain action happens (in this case, the window being resized). I've tried positioning them with CSS and percentages rather than pixels, but it doesn't quite do the job.
Is there a way to store the position of each of those divs in an array and then add/subtract a given amount of pixels?
Here's what I've tried so far - I'm still getting my head around JS so this could be really bad for all I know, but here goes:
roomObjects = $('.object-pos');
var objectCount = 0;
for ( var objectCount = 0; objectCount < 10; objectCount++;) {
roomObjects = rooomObjects[objectCount];
console.log(roomObjects.css("background-position").split(" "));
}
Do you mind sharing why percentages wouldn't work? Usually that's what I would recommend if you're wanting the page to scale correctly on window resizes. I guess if you really wanted to you could do something like:
$(window).resize(function() {
$('#whateverdiv').style.whateverproperty = $('#whateverdiv').style.whateverproperty.toString() + (newPosition - oldPosition);
oldPosition = newPosition;
}
this is obviously not the complete code, but you should be able to fill in the blanks. You'll have to set the oldPosition variable on page load with the original position so that the function works the first time.
edit: you'll also have to strip off the units from the x.style.property string, so that you'll be able to add the value to it
A problem you might well be facing is that when retrieving the current left or top properties, they are returned as a string, with px of % on the end. Try running a parseInt() on the returned values to get a number, then you might well be able to add to the values. Just be sure, when reassigning, that you concatenate "px" or "%" on the end as appropriate.
You could use a bit of jQuery :
var el = $("#id");
var top = el.css("top");
el.css("top", top * 1.2); // increase top by 20%
saves mucking around in the DOM
This might be useful if you want to position things relatively: http://docs.jquery.com/UI/Position

How to know at which character it changes to another line in a textarea?

Text Area:
There is no ENTER at the end of the first line and is there any solution to know at which character it turns to next line?
Take this for instance, it turns to next line after the character "哪" or after the 13th character. (SBC case or DBC case characters can be inputed.)
Well, when you create a textarea you are supposed to supply the number of columns, so if it's wrapping because it's reached the end of the line, it seems fair to assume that the number of characters is the number of columns you have specified. Or am I missing something?
The cols attribute determines the width of the textarea element but is usually overridden by a stylesheet and the width property. Even without a width set by CSS, cols is not strictly enforced by most browsers (in Chrome at least it is accurate as long as the vertical scrollbar is present), and besides lines are usually wrapped at word boundaries not character.
Here's an ugly solution but it should work. The idea is to set the height of the textarea element to 0 then remove words from the end of its value string one at a time, and check the scrollHeight property for changes.
//make scrollbars take up space even when they are not needed
//necessary to keep space available for text constant
txtarea.style.overflow = 'scroll !important';
txtarea.style.height = '0px !important';
var tmpvalue = txtarea.value;
var currentscroll = txtarea.scrollHeight;
var wrapat = [];
var parts;
//Regex breaks textarea value into ['every word but the last', 'one']
while (parts = txtarea.value.match(/([^\0]*)\s\w*/m)) {
txtarea.value = parts[1];
//scrollHeight has changed, add wrap index to array
if (txtarea.scrollHeight < currentscroll) {
wrapat.push(txtarea.value.length-1);
currentscroll = txtarea.scrollHeight;
}
}
//sort wrap array for niceness
wrapat = wrapat.sort(function(a,b) { return a-b; });
//restore everything to normal
txtarea.value = tmpvalue;
txtarea.style.height = '';`
Try the code out here http://jsbin.com/alasa3/edit
There are probably bugs, for instance this code does not handle long "words" such as URL's that might get broken in two. Also it takes a little extra computation to determine if a line was wrapped automaticly or because of a newline (somewhat dealt with in code from link), and it might have trouble with words seperated by multiple spaces/newlines. But that's the basic idea.
If there is an easier solution I'm not aware of it.

Categories