ChrisRockGM
ChrisRockGM

Reputation: 438

How do I filter an array based on a range of numbers?

I have an array that I filter with this function:

function filter(arr, criteria) {
    return arr.filter(function(obj) {
        return Object.keys(criteria).every(function(c) {
            return !(criteria[c]) || obj[c] == criteria[c];
        });
    });
}

var arr = filter(arr, { dep: dv, arr: av, car: cv, epi: acv, dur: dv }); 

And I have a bunch of options that the user can choose from in the select. For duration, here is what I have:

<select name="duration" id="duration">
  <option selected disabled hidden value="">-</option>
  <option value="l1">Less than 1 hour</option>
  <option value="1to3">1 to 3 hours</option>
  <option value="3to6">3 to 6 hours</option>
  <option value="6to10">6 to 10 hours</option>
  <option value="m10">More than 10 hours</option>
</select>

But that filter is based on exact criteria. I want to filter float numbers in arr that are between 1 and 3 or 6 and 10. I also want to be able to run my other filters that you see up there with dep, arr, car, epi, dur. Is there any way I can do this?

Upvotes: 11

Views: 10135

Answers (2)

M&#39;sieur Toph&#39;
M&#39;sieur Toph&#39;

Reputation: 2676

Assuming I understood your needs, what about that :

jsFiddle

HTML :

<select name="duration" id="duration">
    <option selected value="">Duration</option>
    <option value="l1">Less than 1 hour</option>
    <option value="1to3">1 to 3 hours</option>
    <option value="3to6">3 to 6 hours</option>
    <option value="6to10">6 to 10 hours</option>
    <option value="m10">More than 10 hours</option>
</select>
<select name="acceleration" id="acceleration">
    <option selected value="">Acceleration</option>
    <option value="l2">Less than 2m/s²</option>
    <option value="2to5">2 to 5m/s²</option>
    <option value="5to10">5 to 10m/s²</option>
    <option value="10to15">10 to 15m/s²</option>
    <option value="m15">More than 15m/s²</option>
</select>

The data :

var data = [
    {duration:1.2, acceleration:5.7, name:'elt1'},
    {duration:12, acceleration:1.5, name:'elt2'},
    {duration:3, acceleration:7.8, name:'elt3'},
    {duration:5.4789, acceleration:9.6597, name:'elt4'},
    {duration:0.5, acceleration:12.154, name:'elt5'},
    {duration:(15/7), acceleration:20.32, name:'elt6'},
    {duration:9.81, acceleration:7.123, name:'elt7'},
    {duration:10, acceleration:0.3265, name:'elt8'},
    {duration:5, name:'elt9'} //never selected if there is an "acceleration" filter 
    ];

JS :

var filterData = function(arr, criterias){

    // First : parse the criterias
    var realCriterias = {}, tmp;
    var lessThan = /^l(.+)$/, between = /^(.+)to(.+)$/, moreThan = /^m(.+)$/;
    for (var i in criterias){
        if ("" === criterias[i]);
        tmp = criterias[i].match(lessThan);
        if (tmp) { 
            realCriterias[i] = {max:parseFloat(tmp[1])}; 
            continue;
        }
        tmp = criterias[i].match(between);
        if (tmp) { 
            realCriterias[i] = {min:parseFloat(tmp[1]), max:parseFloat(tmp[2])}; 
            continue;
        }
        tmp = criterias[i].match(moreThan);
        if (tmp) { 
            realCriterias[i] = {min:parseFloat(tmp[1])}; 
        }
    }
    console.log('Real criterias', realCriterias);

    // Then : apply them to arr
    var results = [], elt, cri;
    nextdata:
    for (var i=0; i< arr.length; i++){
        elt = arr[i];
        for (var j in realCriterias){
            if (!elt[j]) continue nextdata; // if criteria does not exits on elt, we break this loop and start again with next element
            cri = realCriterias[j];
            if ((undefined === cri.min && elt[j] >= cri.max) || //"less than" criteria and value is too big
                (undefined === cri.max && elt[j] <= cri.min) || //"more than" criteria and value is too small
                (elt[j] > cri.max || elt[j] < cri.min) // "between" criteria and value is not between min and max (included)
               ) {
                continue nextdata; // we break this loop and start again with next element
            }
        }
        results.push(elt);
    }

    return results;

}

var criterias = {
    duration:$('#duration').val()||'', 
    acceleration:$('#acceleration').val()||''
};
var results = filterData(data , criterias);

It is quite a big function, but it is completely scalable. You can use any values and any criterias you need, as long as you respect the syntax l<max>, <min>to<max> and m<min> in your <select> values.

The first part of the function parses the criteria values to get min or/and max value for each of them. And the second part is just about comparing each value of the data array to them.

Do not hesitate if you have any questions.

Upvotes: 1

Zaq
Zaq

Reputation: 1348

Use array.filter with a callback. The code below uses a dictionary with entries corresponding to option values.

var myArray = [1,2,3,3.1,Math.PI,4,4.3,6.1]; //sample array
//Dictionary from option values to functions (JS objects work like hashtables)
var options = {
  "l1":   function(a){return a.duration<1;},
  "1to3": function(a){return a.duration>=1 && a.duration<3;},
  "3to6": function(a){return a.duration>=3 && a.duration<=6;},
...
//When you need to filter the array, do it like so:
var myNewArray = myArray.filter(
  options[ document.getElementById('duration').selectedOptions[0].value ]
);

Upvotes: 8

Related Questions