I have a multidimensional array, which I'm using as a very simple coordinate system. To generate random coordinates, I came up with this very simple function:
var coords = [
[1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1],
[0,0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1],
[1,0,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1],
[1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1],
[1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1],
[0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,1],
[1,0,1,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,0,1,1,0,1,1]
];
function getRandomInt( min, max ) {
return Math.floor( Math.random() * (max - min + 1) ) + min;
}
function randomCoords() {
var x, y;
do {
x = getRandomInt( 0, coords[ 0 ].length - 1 );
y = getRandomInt( 0, coords.length - 1 );
}
while ( coords[ y ][ x ] !== 1 );
return [ x, y ];
}
As you might see, I only want to get random coordinates that are 1 in my array. Although this is working, I was wondering if there's a better / more effective way to do it? Sometimes (especially if there are lots of 0s in my coordinate system) it takes a bit to return a value. In that time (as far as I know) javascript can't do anything else... so everything will just pause...
If you are looking to get a random coordinate only once or twice, then your solution is the best.
If you use it often, you can put the coordinates of the 1's in an array. So you will only have to use random() once on the array
coordPairs1[Math.floor(Math.random() * coordPairs1.length)]
var coords = [
[1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1],
[0,0,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1],
[1,0,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1],
[1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1],
[1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1],
[0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,1],
[1,0,1,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,0,1,1,0,1,1]
];
// make coord-pairs:
var coordPairs1 = []
for(var x=0; x<coords[0].length; ++x) {
for(var y=0; y<coords.length; ++y) {
if(coords[y][x] == 1)
coordPairs1.push([x,y])
}
}
function randomCoords() {
return coordPairs1[Math.floor(Math.random() * coordPairs1.length)]
}
// Example:
document.body.innerHTML = randomCoords()
The Goal
I'm attempting to render a long series of data (around 200 ticks, from small float values like 1.3223) into a line chart.
The Issue
When I use a series of data that changes only a small amount (around 0.0001 every tick), the chart is rendered as very jagged (scissor like). I would like to somehow fix it to have a "saner" radius between each point on the graph.
A Good Example
On the other hand, when rendering higher values (around 1382.21) with bigger difference between ticks (from 0.01 to 0.05 +/-) the graph is rendered more smooth and aesthetically pleasing.
Edit: As user Arie Shaw pointed out, the actual low or high values don't make a difference and it remains an issue of representing small "monotonous" changes is a less jagged form.
The Code
var initChart = function(data, container) {
new Highcharts.Chart({
chart: {
type: "area",
renderTo: container,
zoomType: 'x'
},
title: {
text: ''
},
xAxis: {
labels: {
enabled: false
}
},
yAxis: {
title: {
text: ''
}
},
legend: {
enabled: false
},
color: '#A3D8FF',
plotOptions: {
area: {
fillColor: '#C6E5F4',
lineWidth: 1,
marker: {
enabled: false
},
shadow: false,
states: {
hover: {
lineWidth: 1
}
},
threshold: null
}
},
exporting: {
enabled: false
},
series: [{
name: "TEST",
data: data
}]
});
};
Both graphs, and sample data sets are presented in the following fiddle:
http://jsfiddle.net/YKbxy/2/
The problem you're experiencing is unavoidable: If you have a lot of small variations over time, the graph is going to appear jagged provided that you show each data point.
The key point is that last phrase.
One way to 'smooth out' the rough parts would be to average the data. For example:
var myData = []; //... Some array of data; assuming just numbers
var averageData = function (data, factor) {
var i, j, results = [], sum = 0, length = data.length, avgWindow;
if (!factor || factor <= 0) {
factor = 1;
}
// Create a sliding window of averages
for(i = 0; i < length; i+= factor) {
// Slice from i to factor
avgWindow = data.slice(i, i+factor);
for (j = 0; j < avgWindow.length; j++) {
sum += avgWindow[j];
}
results.push(sum / avgWindow.length)
sum = 0;
}
return results;
};
var var initChart = function(data, container) {
new Highcharts.Chart({
series: [{
name: "TEST",
data: averageData(myData, 2)
}]
});
});
This method also has the advantage that you could (potentially) reuse the function to compare the averaged data to the regular data, or even toggle between how much to average the data.
You can always use areaspline instead of area, see: http://jsfiddle.net/YKbxy/3/
why dont you treat you .00001 data as 1, so times 10000, and then write it in your legend like that.
You should even do that as a test, since if the chart looks fine then, it means there is a problem in the dataset numbers when you return it to normal, since high charts takes the difference between high and low...
Either you must approximate your data by only using a few decimal places or you must average out the values using something like:
var data = new Array(200);
var smallArray = new Array(5);
var averagedData = new Array(20);
for (var index=0; index<=averagedData.length; index++){
for(var i = 0; i<=smallArray.length; i++){
smallArray[i] = data[i + index * 5];
}
averagedData[index] = (smallArray[1] + smallArray[2] + smallArray[3] + smallArray[4] + smallArray[5])/smallArray.length;
}
Then you will only need to plot 20 averaged points on an array of 200 data points. You can change the values for what you need.
In the end the issue is in the frequency of the points or their plotting on yAxis.
When I provide more realistic positioning (e.g timestamp) it will look good.
Meaning that jaggedness is a result of the small changes over constant yAxis progression, which is most similar to nt3rp's answer
I have an array of degrees, [10, 90, 200, 280, 355] for a circle.
I'm given a degree, let's say 1. How do I determine that 1 is closest to 355 degrees?
Subtract the two numbers. If the difference is larger above 180 [or below -180], subtract [or add] 360. Now you can just compare absolute values of the difference.
Here is an actual formula:
degreediff = min(abs(x-y),360-abs(x-y))
This is more compact and efficient:
function difference(a, b) {
var d = Math.abs(a - b);
return d > 180 ? 360 - d : d;
};
function closest(a, bs) {
var ds = bs.map(function(b) { return difference(a, b); });
return bs[ds.indexOf(Math.min.apply(null, ds))];
};
> difference(1, 355)
6
> closest(1, [10, 90, 200, 280, 355])
355
You have one value which will contain the closes degree found found_degree and one for the actual difference degree_difference.
Next, iterate over the whole array and calculate two values: abs(degree_at_position - target_degree) and abs(degree_at_position - 360 - target_degree). If one of those values is smaller than degree_difference, you have a closer degree - store it in found_degree and update degree_difference accordingly.
That's it.
You maybe should initialise found_degree with -1 and degree_difference with 360, just to make sure you can properly interpret the result in case of an empty given array as well - or you simply handle the case of an empty input array separately.
Is this a homework, by the way?
The brute force approach would be something like this:
var closestElement;
var closestDivergence = 360;
var toCompare = 355;
var choices = [1, 90, 200, 280, 355];
for(i=0;i<choices.length;i++){
var currentDivergence=choices[i] - toCompare;
if (currentDivergence<0) {
currentDivergence+=360;
}
if (currentDivergence < closestDivergence){
closestDivergence = currentDivergence;
closestElement = i;
}
}
if (closestElement != NaN){
alert('Closest value is '+choices[closestElement]);
}
Here's a nice little quickie
function closest(deg,ar) {
return ar.sort(function(a,b){var c = deg; return Math.min(360 - (a-c),Math.abs(a-c)) - Math.min(360 - (b-c),Math.abs(b-c))})
}
var myArray = [355, 280, 200, 181, 90, 30];
alert(closest(180,myArray));
Sorts and returns the array according to which one is closest to the provided degree. Index 0 is closest. It does wrap making 355 closer to 0 than 10.
First check the array (check which element is closest) using the given degree (1 in your example), then add 360 and check with that degree (361). Compare which result is better:
x the given degree, y the first result, z the second result
if (abs(x-y) < 360+x-z)
choose y;
else
choose z;
If the array is sorted you can check it with a binary sort which gives you O(log n) time in worst case scenario. Otherwise you have to browse through the whole array two times.
This formula will only work with circles. It's pseudo code of course.
degree diff = min(abs(x-y),360-abs(x-y))
Using the comments from this page I managed to come up with this code:
function closest(deg, degs) {
deg = (deg / 360 > 1 ? deg - (Math.floor(deg / 360)*360) : deg);
var difference = 360;
var closest = -1;
for(i=0;i<degs.length;i++) {
var x = degs[i];
var diff = Math.min(Math.abs(x-deg),360-Math.abs(x-deg))
if(diff <= difference) {
closest = i;
difference = diff;
}
};
return closest;
}
closest(1000, [10, 90, 200, 280, 355]);