2

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}

3
  • 3
    your range would be better described as [[0, 10], [11, 20], [21, 30], [31, 40], [41, 50], [51, 60], [61, Number.POSITIVE_INFINITY]] Commented May 21, 2014 at 23:02
  • (or [{min: 0, max: 10, label: '0-10'}, ...]) Commented May 21, 2014 at 23:03
  • 1
    And, what exactly is the question you want help with? There is no actual question in your question. Commented May 21, 2014 at 23:05

6 Answers 6

1
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/

Sign up to request clarification or add additional context in comments.

Comments

1

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 ] } ]

http://jsbin.com/fowop/1/edit

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 } ]*/

Comments

0

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}

Comments

0

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

Comments

0

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);

Demo.

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.