JavaScript arrays - efficiently calculate average in given intervals - javascript

I have two arrays, one of them represents data, and the other one - intervals. Both are sorted and their start and end values match. I go through nested for loops to calculate the average of data points in a given interval. As a result, I end up with one data value for each interval. For smaller size arrays, < 100-500 length, these linear loops do the work, however, this approach becomes an issue with several thousand data points. Any recommendation will be appreciated.
Please see a simplified code below with a link to JSfiddle at the end
var TimelineArray = [0, 10, 20, 30, 40, 40, 60, 70, 80, 90, 100],
DataArray = [0, 2, 4, 5, 8, 11, 19, 22, 24, 25, 30, 31, 38, 39, 51, 56, 57, 58, 59, 64, 74, 76, 89, 91, 92, 94, 98, 100],
DataArrayA = [];
for (i = 0; i < TimelineArray.length-1; i++) {
var dataPointsInGivenTimeInterval = [];
for (j = 0; j < DataArray.length; j++) {
if (DataArray[j] > TimelineArray[i] && DataArray[j] <= TimelineArray[i+1]) {
dataPointsInGivenTimeInterval.push(DataArray[j]);
}
};
if (dataPointsInGivenTimeInterval.length == 0) {
DataArrayA.push(null);
}
else {
var sumOfdataPoints = null;
for (k = 0; k < dataPointsInGivenTimeInterval.length; k++) {
sumOfdataPoints += dataPointsInGivenTimeInterval[k];
}
var avg = sumOfdataPoints / dataPointsInGivenTimeInterval.length;
DataArrayA.push(avg);
}
} // end for
console.log(TimelineArray);
console.log(DataArrayA);
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}
The console output is
[0, 10, 20, 30, 40, 40, 60, 70, 80, 90, 100]
[4.75, 15, 25.25, 36, null, 56.2, 64, 75, 89, 95]
Here is the code at JSfiddle - calculating average values for given intervals

Since the arrays are sorted, you can do it linearly with respect to the size of the timeline and data:
var timeline = [0, 10, 20, 30, 40, 40, 60, 70, 80, 90, 100],
data = [0, 2, 4, 5, 8, 11, 19, 22, 24, 25, 30, 31, 38, 39, 51, 56, 57, 58, 59, 64, 74, 76, 89, 91, 92, 94, 98, 100];
var averages = new Array(timeline.length - 1);
for (var i = 0, j = 0; i < timeline.length; i++) {
var sum = 0,
items = 0;
for (; data[j] <= timeline[i]; j++) {
sum += data[j];
++items;
}
if(i) averages[i-1] = sum / items;
}
console.log(averages);
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}

You don't need to re-scan DataArray from the beginning on each iteration.
var TimelineArray = [0, 10, 20, 30, 40, 40, 60, 70, 80, 90, 100];
var DataArray = [0, 2, 4, 5, 8, 11, 19, 22, 24, 25, 30, 31, 38, 39, 51, 56, 57, 58, 59, 64, 74, 76, 89, 91, 92, 94, 98, 100];
var res = [], pos = 0;
TimelineArray.forEach(function(v, i) {
for(var sum = 0, n = 0; DataArray[pos] <= v; n++) {
sum += DataArray[pos++];
}
i && res.push(n ? sum / n : null);
});
console.log(res);

Not sure if it'll be any faster, but here's a crack at it in a different way:
var TimelineArray = [0, 10, 20, 30, 40, 40, 60, 70, 80, 90, 100],
DataArray = [0, 2, 4, 5, 8, 11, 19, 22, 24, 25, 30, 31, 38, 39, 51, 56, 57, 58, 59, 64, 74, 76, 89, 91, 92, 94, 98, 100],
DataArrayA = [];
function avg(arr){
if(arr!= null && arr.length > 0)
return arr.reduce(function(a, b){ return a+b;}, 0) / arr.length;
return null;
}
for(var i = 0; i < TimelineArray.length-1; i++){
var interval = [TimelineArray[i], TimelineArray[i+1]];
var data = DataArray.filter(function(a){ return a > interval[0] && a <= interval[1]});
DataArrayA.push(avg(data));
}
console.log(DataArrayA);
edit 1: removed a loop.

Related

How to remove certain elements from the inner array in JavaScripts

var array = [[10, 20, 30, 20, 50], [40, 50, 60, 20, 20], [70, 80, 90, 20, 20], [70, 80, 90, 20, 20]];
For example i want to delete elements == 50
I want this result -> array= [[10,30,20], [40,60,20], [70,90,20], [70,90,20]];
I try this solution but it is not working ->
for (var i = 0; i < array.length; i++) {
for (var j = 0; j < array[i].length; j++) {
if (array[i][j] == 50) {
array[i].splice(j, 1);
}
}
}
You need to collect unwanted indices first and then return.
const
data = [
[10, 20, 30, 20, 50],
[40, 50, 60, 20, 20],
[70, 80, 90, 20, 20],
[70, 80, 90, 20, 20]
],
// ^^ ^^ cols with 50
//
indices = data.reduce(
(r, a) => a.map((v, i) => v === 50 ? false : r[i] ?? true),
[]
),
result = data.map(a => a.filter((_, i) => indices[i]));
result.forEach(a => console.log(...a));
const array = [[10, 20, 30, 20, 50], [40, 50, 60, 20, 20], [70, 80, 90, 20, 20], [70, 80, 90, 20, 20]];
const filteredArr = array.map(item => [...new Set(item.filter(i => i !== 50))])
// filterdArr = [[10, 30, 20], [40, 60, 20], [70, 80, 90, 20], [70, 80, 90, 20]];

Reassign variables in if/else (Javascript)

How can I achieve a reassigned list (or any other value for that matter) in javascript?
I've tried to do as I'd do in python, and make a empty list and assign it if my conditions are met, but it seems that javascript will fall right back on what's assigned out of my if/else
My goal is to achieve something like this:
let data1 = [];
let data2 = [];
let data3 = [];
document.addEventListener("DOMContentLoaded", function(){
if (document.getElementById('minus').checked) {
let data1 = [-64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34];
let data2 = [-23, 45, 83, 74, 99, 83, 64, 72, 50, 44, 36, 45];
let data3 = [-64, 75, 43, 24, 63, 72, 88, 24, 84, 93, 45, 60];
} else {
let data1 = [64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34];
let data2 = [23, 45, 83, 74, 99, 83, 64, 72, 50, 44, 36, 45];
let data3 = [64, 75, 43, 24, 63, 72, 88, 24, 84, 93, 45, 60];
}
});
//Further code that uses the variables we just set
fiddle on my issue:
https://jsfiddle.net/vk8swjc3/
You're currently redefining all of those data variables by saying let again. Remove the lets and do data1 = [-64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34] directly to reassign them.
Two problems:
If you use let x inside a block, you're creating a new variable x in the inner scope. Remove let and you're reassigning the original x.
If you reassign them inside a DOMContentLoaded callback, the new values won't be available to the immediately running code after you set up that callback (only in the callback itself, or once the callback has run). See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
Here's a demo of the order of execution:
console.log('top')
document.addEventListener("DOMContentLoaded", () => {
console.log('DOMContentLoaded')
executeWithinCallback()
})
const executeWithinCallback = () => {
console.log('after DOMContentLoaded')
}
console.log('bottom')
When you are use var, let or const on a scope(curly braces) you are defined a variable that you can use only inside of that scope.
So if you want to use top level variable in another scope then you shouldn't use var, let or const to assign a value to variable.
You can look here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
Having addressed the variable scoping issues that you had in your initial post we can now look at the larger part of your question which seems to be the matter of changing data, or at least setting initial data based on the state of a control.
Your fiddle is working as intended, but not as you expect it to. If you console.log() any of the data arrays after the conditional within the DOMContentLoaded you will see the updated array.
let data1 = [];
let data2 = [];
let data3 = [];
document.addEventListener("DOMContentLoaded", function(){
if (document.getElementById('minus').checked) {
data1 = [-64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34];
data2 = [-23, 45, 83, 74, 99, 83, 64, 72, 50, 44, 36, 45];
data3 = [-64, 75, 43, 24, 63, 72, 88, 24, 84, 93, 45, 60];
} else {
data1 = [64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34];
data2 = [23, 45, 83, 74, 99, 83, 64, 72, 50, 44, 36, 45];
data3 = [64, 75, 43, 24, 63, 72, 88, 24, 84, 93, 45, 60];
}
console.log(data1);
});
// [64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34]
However, by the time that runs you have already set up your chart with the empty array values and it has rendered. Changing the values of the data arrays after that will have no effect on the rendered chart, nor on the values of the arrays assigned to chartData.datasets[n].data
In order to update the chart you need to change these dataset values and call update on the the chart.
let barVertical = document.getElementById("barVertical");
let barVerticalChart = new Chart(barVertical, {
type: "bar",
data: chartData,
options: chartOptions,
});
const minus = document.getElementById('minus');
minus.addEventListener('change', (e) => {
if (e.target.checked) {
chartData.datasets[0].data = [-64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34];
chartData.datasets[1].data = [-23, 45, 83, 74, 99, 83, 64, 72, 50, 44, 36, 45];
chartData.datasets[2].data = [-64, 75, 43, 24, 63, 72, 88, 24, 84, 93, 45, 60];
} else {
chartData.datasets[0].data = [64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34];
chartData.datasets[1].data = [23, 45, 83, 74, 99, 83, 64, 72, 50, 44, 36, 45];
chartData.datasets[2].data = [64, 75, 43, 24, 63, 72, 88, 24, 84, 93, 45, 60];
}
barVerticalChart.update();
});
Updated fiddle: https://jsfiddle.net/q3tmon21/
Or, to simply make your code run as you seem to expect, using the conditionally set arrays on the first render, simply move all the code that comes after it into the DOMContentLoaded listener.
let data1 = [];
let data2 = [];
let data3 = [];
document.addEventListener("DOMContentLoaded", function(){
if (document.getElementById('minus').checked) {
data1 = [-64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34];
data2 = [-23, 45, 83, 74, 99, 83, 64, 72, 50, 44, 36, 45];
data3 = [-64, 75, 43, 24, 63, 72, 88, 24, 84, 93, 45, 60];
} else {
data1 = [64, 31, 23, 34, 23, 30, 50, 43, 29, 64, 22, 34];
data2 = [23, 45, 83, 74, 99, 83, 64, 72, 50, 44, 36, 45];
data3 = [64, 75, 43, 24, 63, 72, 88, 24, 84, 93, 45, 60];
}
let labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
let chartData = {
labels: labels,
datasets: [
{
label: "Data1",
data: data1,
backgroundColor: "rgba(154, 18, 179, .7)",
},
{
label: "Data2",
data: data2,
backgroundColor: "rgba(232, 126, 4, .7)",
},
{
label: "Data3",
data: data3,
backgroundColor: "rgba(0, 230, 64, .7)",
},
],
};
let barVertical = document.getElementById("barVertical");
let barVerticalChart = new Chart(barVertical, {
type: "bar",
data: chartData,
options: chartOptions,
});
});

Javascript declare multiple variables with the same values

Maybe I should use some kind of loop or something. But I don't have any idea how to declare this, excluding do it manually.
Is there any solution that will have only a few lines for all of this. Because, there should be 100 variable, despite I only presented 5, I didn't want to type all night.
a1=1; a2=1; a3=1; a4=1; a5=1;
function myfn1() {
a1++;
//unique function code
}
function myfn2() {
a2++;
//unique function code
}
function myfn3() {
a3++;
//unique function code
}
function myfn4() {
a4++;
//unique function code
}
function myfn5() {
a5++;
//unique function code
}
Although this isn't answering your question, I think this approach will help you more in the long run (and probably in the short run too). Check out using an array, which is just a collection of data. Check out the following code.
var arr = [];
for(var i =0; i <= 100; i++){
arr[i] = i;
}
this gives you a collection like this [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100] that you can then manipulate, etc etc.
*edit or if you want them all to have the same value, then
var arr = [];
for(var i =0; i <= 100; i++){
arr[i] = 1;
}

Highcharts series data array

From an ajax call I get the following data back:
18,635,21,177,20,165,22,163,24,162,25,145,19,143,23,139,26,112,27,110,28,104,30,91,29,88,31,68,32,57,36,55,34,53,33,51,35,46,37,44,39,42,43,39,42,39,41,38,38,37,44,36,45,34,48,31,40,31,47,27,49,23,46,21,50,21,52,17,55,17,53,16,51,15,54,12,58,6,57,6,59,4,63,4,56,3,62,2,64,2,100,2,68,1,78,1,60,1,97,1,70,1,65,1,69,1,71,1
Of which every even number should be the key and every odd the value. But I have no idea how to parse it as highcharts data. Somehow I end up with the key being "slice" if I use JSON.parse and the only way I can get it to work normally is by placing it directly into the series data like this (after seperating the odd and even into seperate arrays):
[names[0] + ' years old', parseFloat(values[0])]
Which is great. But then I need to loop through the arrays somehow, pushing everything into the series data and I don't know how to do that. If I make a for loop with this data, how do I insert into the highcharts series data?
If you have that series data in an array, you can process it as follows:
var myData = [18, 635, 21, 177, 20, 165, 22, 163, 24, 162, 25, 145, 19, 143,
23, 139, 26, 112, 27, 110, 28, 104, 30, 91, 29, 88, 31, 68, 32,
57, 36, 55, 34, 53, 33, 51, 35, 46, 37, 44, 39, 42, 43, 39, 42,
39, 41, 38, 38, 37, 44, 36, 45, 34, 48, 31, 40, 31, 47, 27, 49,
23, 46, 21, 50, 21, 52, 17, 55, 17, 53, 16, 51, 15, 54, 12, 58, 6,
57, 6, 59, 4, 63, 4, 56, 3, 62, 2, 64, 2, 100, 2, 68, 1, 78, 1, 60,
1, 97, 1, 70, 1, 65, 1, 69, 1, 71, 1];
var mySeries = [];
for (var i = 0; i < myData.length; i++) {
mySeries.push([myData[i], myData[i + 1]]);
i++
}
Once you have your series data in 'mySeries', you can just set your chart data using:
series:[{
data: mySeries
}]
Alternatively, if you want to add the data after rendering the chart, you can add the series data dynamically using:
chart.series[0].setData(mySeries);
http://jsfiddle.net/Cm3Ps/ (press the 'Add My Data' button).
Actually, the function requires parameter as array of int.
Assume you get a function
drawChartFunction(data) {
// some code here
series: [{ data: data}]
}
You can try it:
array = {9,8,7,6}
var series = [];
for (var i = 0; i < array.length; i++) {
series.push([i, array.[i]]);
}
After the for executed, your series likes
0,9
1,8
2,7
3,6
Then, you call drawChartFunction(series)
So your chart is drawn by using 4 points 0 1 2 3 with their values 9 8 7 6

How to do statistical classification on an array in javascript?

I've got a bunch of numbers in an array and I need to do some statistics on them. I need to know how many of each number there are in the array.
Here's the array:
myArray =
[2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 14, 14, 14, 14, 14, 14, 15, 15, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 27, 27, 28, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 40, 40, 40, 41, 41, 42, 42, 42, 42, 42, 42, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 55, 56, 57, 57, 57, 57, 57, 57, 57, 58, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 61, 61, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, 65, 65, 66, 66, 66, 67, 67, 67, 68, 68, 68, 69, 69, 69, 69, 69, 69, 70, 70, 71, 71, 71, 71, 71, 71, 71, 72, 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76, 77, 78, 78, 79, 79, 80, 80, 81, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 87, 87, 87, 88, 88, 89, 89, 90, 90, 91, 91, 91, 92, 93, 94, 95, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 99, 99, 99, 99, 99, 101, 101, 102, 102, 103, 103, 105, 105, 105, 106, 107, 107, 108, 108, 109, 109, 109, 109, 110, 112, 112, 113, 113, 113, 114, 114, 115, 116, 116, 117, 118, 120, 121, 121, 121, 122, 122, 123, 123, 123, 124, 124, 124, 124, 125, 126, 127, 128, 129, 130, 130, 131, 131, 131, 131, 132, 133, 133, 134, 134, 134, 136, 136, 136, 136, 137, 137, 137, 138, 138, 138, 139, 139, 139, 140, 141, 141, 142, 142, 143, 144, 144, 144, 144, 145, 150, 150, 153, 155, 159, 160, 160, 161, 162, 164, 164, 166, 176, 180, 180, 180, 181, 181, 187, 191, 192, 193, 194, 197, 200, 203, 211, 216, 224, 251, 280, 333]
Here's what I'm using to parse through it currently (which is not working very well):
for (var key in myArray){
var obj = myArray[key];
var count = 0;
while(obj < 30){
myArrayStats[0] = count;
obj++;
}
while(obj > 30 && obj < 40){
myArrayStats[1] = count;
obj++;
}
//etc....
}
Creating a new array using object literals would be much nicer and easier to use, but I'm not sure how to do it.
This just works whether your array is sorted or not, but it shouldn't be much slower than any algorithm that takes advantage of the fact that it is sorted anyways:
var myArrayStats = [];
for(var i = myArray.length; i--;)
myArrayStats[myArray[i]] = (myArrayStats[myArray[i]] || 0) + 1;
console.log(myArrayStats[6]); // Outputs 7
console.log(myArrayStats[10]); // Outputs 5
console.log(myArrayStats[20]); // Outputs 5
If you want to do this for only a portion of the original array than use slice() to get the portion of the array you want and then do the same thing as above on that array:
var mySubArray = myArray.slice(0,30);
var myArrayStats = [];
for(var i = mySubArray.length; i--;)
myArrayStats[mySubArray[i]] = (myArrayStats[mySubArray[i]] || 0) + 1;
console.log(myArrayStats[6]); // Outputs 7
console.log(myArrayStats[9]); // Outputs 7
console.log(myArrayStats[10]); // Outputs undefined
It sounds like you have equally spaced bins and want to count how many values fall in each. Since this question was tagged with jQuery, let's use a utility function from that to avoid an explicit loop to show another way to do things. (I guess PaulPRO's approach is superior though.)
function hist(values, min, max, numBins) {
var bins = [];
var range = max - min;
jQuery.each(values, function(i, value) {
var bin = Math.floor(numBins * (value - min) / range);
bin = Math.min(Math.max(bin, -1), numBins) + 1;
bins[bin] = (bins[bin] || 0) + 1;
});
return bins;
}
We can exercise the above code with the following:
function consoleHist(values, min, max, numBins) {
var bins = hist(values, min, max, numBins);
var step = (max - min) / numBins;
jQuery.each(bins, function(i, count) {
var lower = (i - 1) * step + min;
var upper = lower + step;
if (lower < min) {
lower = -Infinity;
}
if (upper > max) {
upper = Infinity;
}
console.log('[' + lower + ', ' + upper + '): ' + (count || 0));
});
}
consoleHist([-10, 0, 11, 29, 30, 59, 60, 1000], 0, 60, 2);
Produces the following output on the console:
[-Infinity, 0): 1
[0, 30): 3
[30, 60): 2
[60, Infinity): 2
myArray = [2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8,
8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11,
11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 14, 14]
for (var a = myArray, b = {}, i = 0; i < myArray.length; i++) b[a[i]] ? b[a[i]]++ : b[a[i]] = 1;
console.log(JSON.stringify(Object.keys(b)
.map(function(c) {
return [c, b[c]]
})));

Categories

Resources