Related
I have an array with many numbers (165) and want to 'simplify' the array to fewer numbers (50). The reduced array should still be representative for the 165 original numbers.
For example:
If I had this array with 8 values [2, 4, 3, 8, 1, 4, 9, 3] and would ask to reduce it to 4 values, I want to receive this array [3, 5.5, 2.5, 6]
Currently I have a function that works when the reduced number and the original number can be divided (as with 8 and 4) but when I try using it with let's say 165 values that should be simplified to 50 values it returns me 54 values:
const array = [28, 71, 64, 116, 128, 8, 78, 172, 142, 96, 12 ... 165 values]
const valuesToSum = Math.floor(array.length / 50); // 50 is the wanted array length
for (let i = 0; i < array.length; i++) {
if (i % valuesToSum !== 0 || i === 0) {
sum += array[i];
} else {
returnArray.push(sum / valuesToSum);
sum = 0;
}
}
return returnArray;
In the end this should be a JavaScript function but if someone could explain me this on a mathematical level it would help me a lot.
In order to get the exact number of groups you want, you can't round off the number of elements to group. For instance if you want to reduce from 165 to 50, some of the groups will have 3 elements, some will have 4.
To do this, use nested loops. The outer loop increments by the size of the group, while the inner loop increments by 1 within the group. The rounding happens when you convert this inner index to the array index.
const array = [28, 71, 64, 116, 128, 8, 78, 172, 142, 96, 12]
function reduceArray(array, newSize) {
const returnArray = [];
const valuesToSum = array.length / newSize;
for (let i = 0; i < array.length; i += valuesToSum) {
let sum = 0;
let j;
let start_i = Math.floor(i);
for (j = start_i; j < Math.min(start_i + valuesToSum, array.length); j++) {
sum += array[j];
}
returnArray.push(sum/(j - start_i));
}
return returnArray;
}
console.log(reduceArray(array, 4));
const bigArray = [];
for (let i = 0; i < 165; i++) {
bigArray.push(Math.floor(Math.random() * 200));
}
let result = reduceArray(bigArray, 50);
console.log(result.length);
console.log(result);
Complete JS newbie here!
I made an Array with a few numbers in it. I added a function that will show me the lowest number. My question can I show the index of the lowest number?(even If I would change the numbers)
Here is my code in case you need it:
function func()
{
var low= 0;
var numbersArr=[29, 26, 44, 80, 12, 15, 40];
for(var i = 0; i <= numbersArr.length;i++)
{
if(numbersArr[i]< numbersArr[i-1])
{
low = numbersArr[i];
}
}
console.log(low);
}
func();
You can also store value of i in one variable. See below code.
function func()
{
var numbersArr=[29, 26, 11, 44, 80, 12, 15, 40,10];
var low = numbersArr[0];
var indexOfLow;
for(var i = 0; i <= numbersArr.length;i++)
{
if(numbersArr[i]< low)
{
low = numbersArr[i];
indexOfLow = i;
}
}
console.log("Lowest number is : "+low);
console.log("Index of lowest number is : "+indexOfLow);
}
func();
My question can I show the index of the lowest number?
You can do
numbersArr.indexOf(low)
Edit
That said, your logic of finding the lowest number isn't correct as you are only comparing the consecutive values of the array, try the updated logic in demo below.
Demo
function func() {
var numbersArr = [29, 26, 11, 44, 80, 12, 15, 40];
var low = numbersArr[0];
for (var i = 1; i <= numbersArr.length; i++) {
if (numbersArr[i] < low ) {
low = numbersArr[i];
}
}
console.log(low);
console.log(numbersArr.indexOf(low));
}
func();
You function lack the problem of keeping the lowest value, because you compare only the actual element and the element before.
function getLowestValue(array) {
var low = 0,
i;
for (i = 0; i < array.length; i++) { // just loop i < array.length!
if (array[i] < array[i - 1]) {
// ^^^^^^^^^^^^ this is the problem, you need to check against low
low = array[i];
}
}
return low;
}
console.log(getLowestValue([29, 26, 11, 80, 12, 15, 40])); // 12 instead of 11
You could store the value of the lowest element and compare with this value.
function getLowestValue(array) {
var low = array[0], // take the first element at index 0
i;
for (i = 1; i < array.length; i++) { // start with index 1, because you need to
// check against the last known smallest value
if(array[i] < low) {
low = array[i];
}
}
return low;
}
console.log(getLowestValue([29, 26, 11, 80, 12, 15, 40])); // 11 the right value
For getting the lowest index, you could just store the index instead of th value and take it for comparison.
function getLowestIndex(array) {
var lowIndex = 0,
i;
for (i = 1; i < array.length; i++) {
if (array[i] < array[lowIndex]) {
lowIndex = i;
}
}
return lowIndex;
}
console.log(getLowestIndex([29, 26, 11, 80, 12, 15, 40])); // 2
No need to make a function to find minimum value. You can use simply Math.min.apply to find minimum value from array and then indexOf to find index of that value
var numbersArr = [29, 26, 44, 80, 12, 15, 40];
var minValue = Math.min.apply(null, numbersArr);
console.log("Minimum Value:"+ minValue, "Index is:" + numbersArr.indexOf(minValue));
Solution:You will declair a variable which will hold the index of low number and assign it in the if statement as shown below:
if(numbersArr[i]< numbersArr[i-1])
{
low = numbersArr[i];
IndexLow=i;
}
I found a super easy way to do that, in your way. Just little change in your code. Please take a look.
function func()
{
var numbersArr=[29, 31, 26, 44, 80, 123, 151, 40,15];
var low= numbersArr[0];
var index = 0;
for(var i = 1; i < numbersArr.length;i++)
{
if(low >= numbersArr[i])
{
low = numbersArr[i];
index = i;
}
}
console.log(index);
console.log(low);
}
func();
Hope you found your problem. Happy coding :)
I'm trying to find the efficient way of finding the similar numbers (where the difference between the numbers is "almost constant" - close to some constant).
For example having array like:
const arr = [23, 130, 142, 151, 163, 200];
would return a new array:
const similarNumbers = [130, 142, 151, 163];
Currently the way I try to solve it is to map through the first array and try to find smallest difference between the numbers and then map again and check if the absolute value of currentValue - previousValue - smallestGap < SOME_THRESHOLD, but this is not ideal and only works in some of the particular cases.
Ideally what I'd like to achieve is for the function to return a few arrays with the grouped similar numbers:
const someArr = [23, 130, 142, 151, 163, 200, 232, 261];
would return two arrays:
group1 = [130, 142, 151, 163];
and:
group2 = [200, 232, 261];
This is a simple (and not perfect) function I just wrote. It will group numbers into groups based on a threshold value which defines the maximum derivation from the groups average.
const someArr = [23, 130, 142, 151, 163, 200, 232, 261];
var threshold = 50;
var relations = {};
var groups = {};
i = 0;
for (var j = 0; j < someArr.length; j++) {
var number = someArr[j];
if (i == 0) {
relations[number] = i;
groups[i] = [];
groups[i].push(number);
}
else {
var added = false;
var n_groups = 0;
for (var g in groups) {
var sum = 0;
var group = groups[g];
if (group.length == 1 && Math.abs(number - group[0]) <= 2 * threshold) {
relations[number] = parseInt(g, 10);
groups[g].push(number);
added = true;
}
else {
for( var n = 0; n < group.length; n++ ){
sum += group[n];
}
var avg = sum/group.length;
if (Math.abs(number - avg) <= threshold) {
relations[number] = parseInt(g, 10);
groups[g].push(number);
added = true;
}
}
n_groups++;
}
if (!added) {
var h = n_groups;
relations[number] = parseInt(h, 10);
groups[h] = [];
groups[h].push(number);
}
}
i++;
}
//console.log(relations, groups);
document.getElementById('grouped').innerHTML = JSON.stringify(groups);
<h3>Input</h3><div id="original">[23, 130, 142, 151, 163, 200, 232, 261]</div>
<h3>Output</h3><div id="grouped"></div>
I'm not sure it's the result you want to obtain.
function(entries) {
let groups = {};
let finalGroups = {};
for (var i = 0; i < entries.length; i++) {
let b = entries[i];
let power = 0;
while(b > 10) {
power++;
b = b / 10
}
groups[power] = groups[power] || []
groups[power].push(a[i]);
}
for (let i in groups) {
for (let j = 0; j < groups[i].length; j++) {
const c = groups[i][j]
const max = Math.floor(c / Math.pow(10, i));
finalGroups[i] = finalGroups[i] || {};
finalGroups[i][max] = finalGroups[i][max] || [];
finalGroups[i][max].push(c);
}
}
return finalGroups;
}
Here is the result. You will need to extract arrays but that's not the difficult part
{
"1":
{
"2":[23]
},
"2":
{
"1":[130,142,151,163],
"2":[200,232,261]
}
}
The function will firstly sort entries by their powers of 10. Then it will sort them by the number the most on the left.
So if you have values like 1030 and 1203 they will be considered as close values...
EDIT: Made a mistake. Fixed it
If you're wanting to find two groups of numbers that are more alike with each other, for the simplest sets you can split them in half, and find the difference between the first/last items, and the difference between the item before/after that item, if its closer to the item in the other group, else leave them as they are
const someArr = [23, 130, 142, 151, 163, 200, 232, 261];
someArr.sort((a, b) => a < b ? -1 : 1);
let groupA = someArr.slice(0, Math.ceil(someArr.length / 2))
let groupB = someArr.slice(Math.ceil(someArr.length / 2));
const differenceInA = Math.abs(groupA[groupA.length - 2] - groupA[groupA.length - 1]);
const differenceInB = Math.abs(groupB[0] - groupB[1]);
const differenceInBoth = Math.abs(groupA[groupA.length - 1] - groupB[0])
if (differenceInBoth < differenceInB) {
groupA.push(groupB[0]);
groupB = groupB.slice(1);
} else if(differenceInBoth > differenceInA) {
groupB.unshift( groupA[groupA.length - 1]);
groupA = groupA.slice(0, groupA.length - 1);
}
console.log('Group A:', groupA);
console.log('Group B:', groupB);
The above logs
Group A: [
23,
130,
142,
151,
163
]
Group B: [
200,
232,
261
]
I have two separate arrays: one with the hour at which the measurement was performed and one with the results obtained at that hour. Example: (the real data is much longer)
hours =[10,8,13,7,8,12,10,13,23,12]
results =[101, 104, 101, 106, 101, 107, 109, 110, 112, 107]
I plot one against the other and now I need to find the average for each hour. At first I thought this to be a trivial thing to do but it got complicated real fast. I bet there is an easy way to do it, I just can't find it. I searched couple of related questions about it and came up with a solution below. It works but it is slow and surely there is a way to do it better. (Note that for plotting I need to end up with sorted arrays for unique hours and equivalently sorted array containing the averages for each hour).
function ave (a) {
var total = 0;
for(var i = 0; i <= a.length-1; i++) {
total += a[i];
}
return total / a.length;
}
var unique = Array.from(new Set(hours));
unique.sort(function(a, b){return a-b});
var arr_res= [];
var build_arr =[];
for (var i = 0; i <= unique.length-1; i++) {
build_arr.length = 0;
for (var j = 0; j <= results-1; j++) {
if (hours[j] == unique[i]) {
build_arr.push(results[j]);
}
}
arr_res.push(ave(build_arr));
}
1. Iterate over hours array
2. Create an object that would have at it's key the value of hour
3. The value would be the value from results array when you are adding the key first time.
4. When that same key is found subsequently, the average is computed and added as modified value.
5. Push the obj values into an array by iterating the object
6. Sort the array by hour. If this array is enough stop else collect the result in two arrays.
See the solution below:
var hours = [10, 8, 13, 7, 8, 12, 10, 13, 23, 12];
var results = [101, 104, 101, 106, 101, 107, 109, 110, 112, 107];
var obj = {};
var avg = 0,
tempCount;
hours.forEach(function(v, i) {
if (!obj[v]) {
obj[v] = {
hour: v,
val: results[i],
count: 1
};
} else {
tempCount = obj[v].count + 1;
avg = (obj[v].val + results[i]) / tempCount;
obj[v] = {
hour: v,
val: avg,
count: tempCount
};
}
});
//You have got averages by now. //Only thing remaining is to get the right output //data structures.
var res = [];
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
//console.log("Key=" + i, "Val=" + obj[i].val);
res.push(obj[i]);
}
}
res = res.sort(function(a, b) {
return a.hour - b.hour;
});
var hourRes = [],
avgRes = [];
res.forEach(function (v, i) {
hourRes.push(v.hour);
avgRes.push(v.val);
});
console.log(hourRes, avgRes);
You can do it something like this by combining Array.reduce(), Array.map() and Object.keys() :
var hours =[10,8,13,7,8,12,10,13,23,12];
var results =[101, 104, 101, 106, 101, 107, 109, 110, 112, 107];
var groups = hours.reduce(function(acc, hour, idx) {
acc[hour] = acc[hour] || [];
acc[hour].push(results[idx]);
return acc;
}, {});
//console.log(groups);
var averages = Object.keys(groups).sort(function(a, b) {
return a-b;
}).map(function(key) {
return groups[key].reduce(function(sum, val) {
return sum+val;
}) / groups[key].length;
});
console.log(Object.keys(groups).sort(function(a, b){return a - b;}));
console.log(averages);
If I understand the question correctly then the following should work:
Output for the following is :
**
7-106, 8-102.5, 10-105, 12-107, 13-105.5, 23-112
**
var hours = [10, 8, 13, 7, 8, 12, 10, 13, 23, 12];
var results = [101, 104, 101, 106, 101, 107, 109, 110, 112, 107];
function toObject(names, values) {
var result = [];
for (var i = 0; i < names.length; i++)
result.push({ hour: names[i], result: values[i]});
return result;
}
function average(arr) {
var sums = {}, counts = {}, results = [], name;
for (var i = 0; i < arr.length; i++) {
name = arr[i].hour;
if (!(name in sums)) {
sums[name] = 0;
counts[name] = 0;
}
sums[name] += arr[i].result;
counts[name]++;
}
for(name in sums) {
results.push({ name: name, value: sums[name] / counts[name] });
}
return results;
}
function Test() {
var result = toObject(hours, results);
var averagess = average(result);
for (var i = 0; i < averagess.length; i++) {
console.log(averagess[i].name + '-' + averagess[i].value);
}
}
This is probably an odd question since I have a solution (below), but was hoping someone could show me a more succinct or readable way to do this:
I created a loop that outputs the following array:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91]
the gaps between numbers get progressively larger:
1-0 = 1
3-1 = 2
6-3 = 3
10-6 = 4
...
91-78 = 13
etc.
I did it by creating two variables, step keeps track of the gap size and count keeps track of the current 'position' in the gap. count counts down to zero, then increases step by one.
var output = [];
var step = 0;
var count = 0;
for (var i = 0; i < 100; i++) {
if (count == 0){
step += 1;
count = step;
output.push(i);
}
count -= 1;
}
You can try the following:
var output = [];
var total = 0;
for (var i=1; i < 100; i++) {
output.push(total);
total += i;
}
The gaps between numbers simply increase by one for each step, so a for loop should be able to track this change.
You should skip useless iterations. If you want a sequence of 100 numbers, use
var output = [];
var step = 0;
for (var i = 0; i < 100; i++) {
step += i;
output.push(step);
}
If you want the general term,
aₙ = ∑ⁿᵢ₌₀ i = n*(n+1)/2
So you can also do
var output = [];
for (var i = 0; i < 100; i++) {
output.push(i * (i+1) / 2);
}
You can save the total helper variable with this solution:
var output = [0]
for (var i = 1; i < 14; i++) {
output.push(output[i - 1] + i)
}
console.log(output) // [ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91 ]
This solution takes into account that the value to add the counter value to is already present at the last position in the array.
A recursive version is also possible:
output = (function f(x) {
return x.length == 14 ? x : f(x.concat([x[x.length - 1] + x.length]))
})([0])
console.log(output); // [ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91 ]
Here is no additional counter variable is needed. I use concat because it returns an array what I need for the recursive call, where push returns the new array length. The argument for concat is an array with one element with the new value to add.
Try online