Reputation: 31
I need to check if an array I have is within certain ranges.
I have an array:
var times = [79, 118, 145, 245, 688, 833, 934, 956, 1019, -339, -324, -265, 65, 81, 83, 121, 151, 154, 359]
and want to get the count of values falling within these ranges:
var ranges = ['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61 +'];
Here is the code I have so far..
$.each(times, function (i, datum)
{
if (datum <= 0)
sum1 += 1;
else if (datum > 10 && datum <= 20)
sum2 += 1;
else if (datum > 20 && datum <= 30)
sum3 += 1;
else if (datum > 30 && datum <= 40)
sum4 += 1;
else if (datum > 40 && datum <= 50)
sum5 += 1;
else if (datum > 50 && datum <= 60)
sum6 += 1;
else if (datum > 60)
sum7 += 1;
}
How would I get an object of ranges and sums. e.g. {'0-10': 2, '11-20': 19, '21-30': 45}
Upvotes: 2
Views: 4016
Reputation: 1040
Even if it's an old question I think this deserves an updated answer as javascript has evolved a lot. This is what I would do:
var times = [79, 118, 145, 245, 688, 833, 934, 956, 1019, -339, -324, -265, 65, 81, 83, 121, 151, 154, 359]
var ranges = ['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61'];
const result = {}
ranges.forEach(range=>{
const rangeNumbers = range.split("-")
let inRangeNumbers
if(rangeNumbers.length>1){
inRangeNumbers = times.filter(t=>(t>=parseInt(rangeNumbers[0]) && t<=parseInt(rangeNumbers[1])))
}else{
inRangeNumbers = times.filter(t=>(t>=parseInt(rangeNumbers[0]))
}
result[range] = {range: range, amount: inRangeNumbers.length, values: inRangeNumbers}
})
}
return result
Upvotes: 0
Reputation: 63317
You can try this code:
var times = [79, 118, 145, 245, 688, 833, 15, 934, 956, 1019, -339, -324, -265, 65, 81, 83, 121, 151, 154, 359];
var ranges = ['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61 +'];
var rangeStops = [0,11,21,31,41,51,61];
var rangeCounts = [0];
//sort the times first
times.sort(function(a,b){return a - b});
var k = 0;
for(var i = 0; i < times.length; i++){
if(rangeStops[k] > times[i]) continue;
else if(rangeStops[k+1] <= times[i]&&k != rangeStops.length - 1){
rangeCounts[++k] = 0;
i--;
} else rangeCounts[k]++;
}
//output
ranges = ranges.map(function(e,i){
var result = {};
result[e] = rangeCounts[i];
return result;
});
//log it and see the result in console window
console.log(ranges);
Note that I added a 15
value to the array times
, and the result should be:
'0-10': 0
'11-20': 1 //the 15 value
'21-30': 0
'31-40': 0
'41-50': 0
'51-60': 0
'61+' : 16 //all the other positive values
Upvotes: 0
Reputation: 10064
No SO answer would be complete without an Underscore answer:
var times = [79, 118, 145, 245, 688, 833, 934, 956, 1019, -339, -324, -265, 65, 81, 83, 121, 151, 154, 359]
var ranges = ['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61 +'];
var counts = _(ranges).reduce(function(memo, range) {
memo[range] = 0;
return memo;
}, {});
counts.outOfBounds = 0;
var rangePattern = /(\d+)\s*-\s*(\d+)/;
var moreThanPattern = /(\d+)\s*\+/;
var lessThanPattern = /-\s*(\d+)/;
_(times).forEach(function(value) {
var range = _(ranges).find(function(testRange) {
var match;
switch (false) {
case !(match = testRange.match(rangePattern)):
return (value >= match[1] && value <= match[2]);
case !(match = testRange.match(moreThanPattern)):
return (value >= match[1]);
case !(match = testRange.match(lessThanPattern)):
return (value <= match[1]);
default:
return false;
}
});
counts[range || 'outOfBounds']++;
});
console.log(counts);
See it in action at this JSBin
Upvotes: 0
Reputation: 94101
I would recommend outputting a collection, rather than an object because it will be easier to work with; since it's an array of objects you can filter, map, and reduce, which you can't do very comfortably on objects. For example:
function getStepRanges(ranges, times) {
return ranges.map(function(range) {
var ranges = range.split(/[-+]/)
var min = +ranges[0]
var max = +ranges[1] || Infinity
return times.reduce(function(acc, x) {
// Note that your range is not inclusive
// you may want to do "x >= min" instead
if (x > min && x <= max) {
acc.range = range
acc.values = (acc.values||[]).concat(x)
}
return acc
},{})
})
}
Using it on this dummy data:
var times = [1,2,3,4,5,10,11,12,13,14,21,23,24,25,26,31,32,33,34,35,41,42,43,44,45,51,52,53,54,55,61,62,63]
var ranges = ['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61+']
It will return this collection:
[ { range: '0-10', values: [ 1, 2, 3, 4, 5, 10 ] },
{ range: '11-20', values: [ 12, 13, 14 ] },
{ range: '21-30', values: [ 23, 24, 25, 26 ] },
{ range: '31-40', values: [ 32, 33, 34, 35 ] },
{ range: '41-50', values: [ 42, 43, 44, 45 ] },
{ range: '51-60', values: [ 52, 53, 54, 55 ] },
{ range: '61+', values: [ 62, 63 ] } ]
With that data then you can do what you need, for example getting the maximum values:
var result = getStepRanges(ranges, times)
// Easy, we have a collection!
// but careful, it mutates the original object
// you may want to use an "extend" helper to clone it first
var maxRanges = result.map(function(x) {
x.max = Math.max.apply(0, x.values)
return x
})
console.log(maxRanges)
/*[ { range: '0-10', values: [ 1, 2, 3, 4, 5, 10 ], max: 10 },
{ range: '11-20', values: [ 12, 13, 14 ], max: 14 },
{ range: '21-30', values: [ 23, 24, 25, 26 ], max: 26 },
{ range: '31-40', values: [ 32, 33, 34, 35 ], max: 35 },
{ range: '41-50', values: [ 42, 43, 44, 45 ], max: 45 },
{ range: '51-60', values: [ 52, 53, 54, 55 ], max: 55 },
{ range: '61+', values: [ 62, 63 ], max: 63 } ]*/
Upvotes: 1
Reputation: 16068
var times = [79, 118, 145, 245, 688, 833, 934, 956, 1019, -339, -324, -265, 65, 81, 83, 121, 151, 154, 359];
var ranges = ['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61 +'];
function getObj(range){
var obj={};
range=range.replace("+","-Infinity");
var arr=range.split("-");
return {sum:0,min:Number(arr[0]),max:Number(arr[1])};
}
var sums={};
for(var i=0;i<ranges.length;i++) sums[ranges[i]]=getObj(ranges[i]);
for(var i=0;i<times.length;i++){
for(var j=0;j<ranges.length;j++){
var sum=sums[ranges[j]];
if(times[i]>=sum.min && times[i] <= sum.max){sum.sum+=1}
}
}
console.log(sums)
Full fiddle: http://jsfiddle.net/6RRyL/1/
Upvotes: 1
Reputation: 39386
Assuming you rewrite your range as such:
rangeList = $.map(ranges, function(item) {
values = item.split('-')
return {min: parseInt(values[0]), max: parseInt(values[1]) || Number.POSITIVE_INFINITY, label: item}
})
You have a nice structure. Now you have to test each value against each range. (Technically you can stop at the first that match, so you probably can use any
instead of each
and return true if the range is met).
var sums = {}
$.each(times, function(i, datum) {
$.each(rangeList, function(rangeIndex, range) {
if (datum > range.min && datum <= range.max) {
sums[range.label] = (sums[range.label] || 0) + 1
}
})
})
Of course in your case, that returns {61 +: 16}
Upvotes: 0