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
]
Related
I'm doing an online Udemy Course on Backend Development and I can't figure out why I keep getting NaN on this code. I already have a working answer but I want to know if anyone can tell me what I am doing wrong.
function grader(array) {
var total = 0
for (var i = 0; i < array.length; i++) {
var total += total + array.length[i];
}
var avg = total / array.length;
return Math.round(avg);
}
var scores = [98, 90, 89, 100, 100, 86, 94];
console.log(grader(scores));
I expect to have the program print out 94 but I keep getting NaN.
Add the current array[i] to the total:
function grader(array) {
var total = 0
for (var i = 0; i < array.length; i++) {
total += array[i]; // changed from this - var total += total + array.length[i];
}
var avg = total / array.length;
return Math.round(avg);
}
var scores = [98, 90, 89, 100, 100, 86, 94];
console.log(grader(scores));
An easier way would be to use reduce:
function grader(array) {
return Math.round(array.reduce((r, n) => r + n, 0) / array.length);
}
var scores = [98, 90, 89, 100, 100, 86, 94];
console.log(grader(scores));
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);
I am trying to find out the minimum elements in array whose sum equals
the given input.I tried for few input sum but was able to find only a
pair in first case while I need to implement for more than just a pair.
var arr = [10, 0, -1, 20, 25, 30];
var sum = 45;
var newArr = [];
console.log('before sorting = ' + arr);
arr.sort(function(a, b) {
return a - b;
});
console.log('after sorting = ' + arr);
var l = 0;
var arrSize = arr.length - 1;
while (l < arrSize) {
if (arr[l] + arr[arrSize] === sum) {
var result = newArr.concat(arr[l], arr[arrSize]);
console.log(result);
break;
} else if (arr[l] + arr[arrSize] > sum) {
arrSize--;
} else {
l++;
}
}
Input Array : [10, 0, -1, 20, 25, 30]
Required Sum: 45
Output: [20, 25]
I am trying for
Required Sum : 59
Output: [10, -1, 20, 30]
This can be viewed as an optimization problem which lends itself well to dynamic programming.
This means you would break it up into a recursion that tries to find the minimum length of increasingly smaller arrays with the sum adjusted for what's been removed. If your array is [10, 0, -1, 20, 25, 30] with a sum of 59 you can think of shortest as the min of:
[10, ... shortest([ 0, -1, 20, 25, 30], 49)
[0, ... shortest([10, 20, 25, 30], 49), 59)
[-1, ... shortest([10, 0, 20, 25, 30], 60)
... continue recursively
with each recursion, the array gets shorter until you are left with one element. Then the question is whether that element equals the number left over after all the subtractions.
It's easier to show in code:
function findMinSum(arr, n){
if(!arr) return
let min
for (let i=0; i<arr.length; i++) {
/* if a number equals the sum, it's obviously
* the shortest set, just return it
*/
if (arr[i] == n) return [arr[i]]
/* recursively call on subset with
* sum adjusted for removed element
*/
let next = findMinSum(arr.slice(i+1), n-arr[i])
/* we only care about next if it's shorter then
* the shortest thing we've seen so far
*/
if (next){
if(min === undefined || next.length < min.length){
min = [arr[i], ...next]
}
}
}
return min && min /* if we found a match return it, otherwise return undefined */
}
console.log(findMinSum([10, 0, -1, 20, 25, 30], 59).join(', '))
console.log(findMinSum([10, 0, -1, 20, 25, 30], 29).join(', '))
console.log(findMinSum([10, 0, -1, 20, 25, 30], -5)) // undefined when no sum
This is still pretty computationally expensive but it should be much faster than finding all the subsets and sums.
One option is to find all possible subsets of the array, and then filter them by those which sum to the required value, and then identify the one(s) with the lowest length:
const getMinElementsWhichSum = (arr, target) => {
const subsets = getAllSubsetsOfArr(arr);
const subsetsWhichSumToTarget = subsets.filter(subset => subset.reduce((a, b) => a + b, 0) === target);
return subsetsWhichSumToTarget.reduce((a, b) => a.length < b.length ? a : b, { length: Infinity });
};
console.log(getMinElementsWhichSum([10, 0, -1, 20, 25, 30], 45));
console.log(getMinElementsWhichSum([10, 0, -1, 20, 25, 30], 59));
// https://stackoverflow.com/questions/5752002/find-all-possible-subset-combos-in-an-array
function getAllSubsetsOfArr(array) {
return new Array(1 << array.length).fill().map(
(e1,i) => array.filter((e2, j) => i & 1 << j));
}
Try this,
var arr = [10, 0, -1, 20, 25, 30];
var sum = 29;
var newArr = [];
var sum_expected = 0;
var y = 0;
while (y < arr.length) {
for (let i = 0; i < arr.length; i++) {
var subArr = [];
sum_expected = arr[i];
if (arr[i] != 0) subArr.push(arr[i]);
for (let j = 0; j < arr.length; j++) {
if (i == j)
continue;
sum_expected += arr[j];
if (arr[j] != 0) subArr.push(arr[j]);
if (sum_expected == sum) {
var result = arr.filter((el)=>(subArr.indexOf(el) > -1));
!newArr.length ? newArr = result : result.length < newArr.length ? newArr = result : 1;
break;
}
}
}
let x = arr.shift();
arr.push(x);
y++;
}
if (newArr.length) {
console.log(newArr);
} else {
console.log('Not found');
}
Create a function called biggestNumberInArray().
That takes an array as a parameter and returns the biggest number.
Here is an array
const array = [-1, 0, 3, 100, 99, 2, 99]
What I try in my JavaScript code:
function biggestNumberInArray(arr) {
for (let i = 0; i < array.length; i++) {
for(let j=1;j<array.length;j++){
for(let k =2;k<array.length;k++){
if(array[i]>array[j] && array[i]>array[k]){
console.log(array[i]);
}
}
}
}
}
It returns 3 100 99.
I want to return just 100 because it is the biggest number.
Is there a better way to use loops to get the biggest value?
Using three different JavaScript loops to achieve this (for, forEach, for of, for in).
You can use three of them to accomplish it.
Some ES6 magic for you, using the spread syntax:
function biggestNumberInArray(arr) {
const max = Math.max(...arr);
return max;
}
Actually, a few people have answered this question in a more detailed fashion than I do, but I would like you to read this if you are curious about the performance between the various ways of getting the largest number in an array.
zer00ne's answer should be better for simplicity, but if you still want to follow the for-loop way, here it is:
function biggestNumberInArray (arr) {
// The largest number at first should be the first element or null for empty array
var largest = arr[0] || null;
// Current number, handled by the loop
var number = null;
for (var i = 0; i < arr.length; i++) {
// Update current number
number = arr[i];
// Compares stored largest number with current number, stores the largest one
largest = Math.max(largest, number);
}
return largest;
}
There are multiple ways.
Using Math max function
let array = [-1, 10, 30, 45, 5, 6, 89, 17];
console.log(Math.max(...array))
Using reduce
let array = [-1, 10, 30, 45, 5, 6, 89, 17];
console.log(array.reduce((element,max) => element > max ? element : max, 0));
Implement our own function
let array = [-1, 10, 30, 45, 5, 6, 89, 17];
function getMaxOutOfAnArray(array) {
let maxNumber = -Infinity;
array.forEach(number => { maxNumber = number > maxNumber ? number : maxNumber; })
console.log(maxNumber);
}
getMaxOutOfAnArray(array);
The simplest way is using Math.max.apply:
const array = [-1,0,3,100, 99, 2, 99];
function biggestNumberInArray(arr) {
return Math.max.apply(Math, arr);
}
console.log(biggestNumberInArray(array));
If you really want to use a for loop, you can do it using the technique from this answer:
const array = [-1,0,3,100, 99, 2, 99];
function biggestNumberInArray(arr) {
var m = -Infinity,
i = 0,
n = arr.length;
for (; i != n; ++i) {
if (arr[i] > m) {
m = arr[i];
}
}
return m;
}
console.log(biggestNumberInArray(array));
And you could also use reduce:
const array = [-1,0,3,100, 99, 2, 99];
function biggestNumberInArray(array) {
return array.reduce((m, c) => c > m ? c : m);
}
console.log(biggestNumberInArray(array));
I think you misunderstand how loops are used - there is no need to have three nested loops. You can iterate through the array with a single loop, keeping track of the largest number in a variable, then returning the variable at the end of the loop.
function largest(arr) {
var largest = arr[0]
arr.forEach(function(i) {
if (i > largest){
largest = i
}
}
return largest;
}
Of course you can do this much more simply:
Math.max(...arr)
but the question does ask for a for loop implementation.
This is best suited to some functional programming and using a reduce, for loops are out of favour these days.
const max = array => array && array.length ? array.reduce((max, current) => current > max ? current : max) : undefined;
console.log(max([-1, 0, 3, 100, 99, 2, 99]));
This is 70% more performant than Math.max https://jsperf.com/max-vs-reduce/1
Another visual way is to create a variable called something like maxNumber, then check every value in the array, and if it is greater than the maxNumber, then the maxNumber now = that value.
const array = [-1,0,3,100, 99, 2, 99];
function biggestNumberInArray(arr) {
let maxNumber;
for(let i = 0; i < arr.length; i++){
if(!maxNumber){ // protect against an array of values less than 0
maxNumber = arr[i]
}
if(arr[i] > maxNumber){
maxNumber = arr[i];
}
}
return maxNumber
}
console.log(biggestNumberInArray(array));
I hope this helps :)
var list = [12,34,11,10,34,68,5,6,2,2,90];
var length = list.length-1;
for(var i=0; i<length; i++){
for(j=0; j<length; j++){
if(list[j]>list[j+1]){
[ list[j] , list[j+1] ] = [ list[j+1] , list[j] ];
}
}
}
console.log(list[list.length-1]);
You Can try My codes to find the highest number form array using for loop.
function largestNumber(number){
let max = number[0];
for(let i = 0; i < number.length; i++){
let element = number[i];
if(element > max){
max = element;
}
}
return max;
}
let arrayNum= [22,25,40,60,80,100];
let result = largestNumber(arrayNum);
console.log('The Highest Number is: ',result);
let arr = [1,213,31,42,21];
let max = 0;
for(let i = 0; i < arr.length; i++) {
if(arr[i] > max) {
max = arr[i]
}
}
console.log(max)
There are multiple ways.
way - 1 | without for loop
const data = [-1, 0, 3, 100, 99, 2, 99];
// way - 1 | without for loop
const maxValue = Math.max(...data);
const maxIndex = data.indexOf(maxValue);
console.log({ maxValue, maxIndex }); // { maxValue: 100, maxIndex: 3 }
way - 2 | with for loop
const data = [-1, 0, 3, 100, 99, 2, 99];
// way - 2 | with for loop
let max = data[0];
for (let i = 0; i < data.length; i++) {
if (data[i] > max) {
max = data[i];
}
}
console.log(max); // 100
THis is the simple function to find the biggest number in array with for loop.
// Input sample data to the function
var arr = [-1, 0, 3, 100, 99, 2, 99];
// Just to show the result
console.log(findMax(arr));
// Function to find the biggest integer in array
function findMax(arr) {
// size of array
let arraySize = arr.length;
if (arraySize > 0) {
// Initialize variable with first index of array
var MaxNumber = arr[0];
for (var i = 0; i <= arraySize; i++) {
// if new number is greater than previous number
if (arr[i] > MaxNumber) {
// then assign greater number to variable
MaxNumber = arr[i];
}
}
// return biggest number
return MaxNumber;
} else {
return 0;
}
}
You can try this if you want to practice functions
const numbs = [1, 2, 4, 5, 6, 7, 8, 34];
let max = (arr) => {
let max = arr[0];
for (let i of arr) {
if (i > max) {
max = i;
}
}
return max;
};
let highestNumb = max(numbs);
console.log(highestNumb);
const array = [-1, 0, 3, 100, 99, 2, 99]
let longest = Math.max(...array);
what about this
const array = [1, 32, 3, 44, 5, 6]
console.time("method-test")
var largestNum = array[0]
for(var i = 1; i < array.length; i++) {
largestNum = largestNum > array[i] ? largestNum : array[i]
}
console.log(largestNum)
console.timeEnd("method-test")
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);
}
}