Skip to content Skip to sidebar Skip to footer

Trouble With Math In Javascript, When Elements Clicked

Unsolved Last updated: 4:15 p.m. Where I'm at I have three categories for people to choose: Gender, Age, Ethnicity. Each with their own set of options Male/Female; under 35, 36-64,

Solution 1:

If I've understood it correctly the following demo should do what you want. You can find the code below and here at jsFiddle. (old version of the jsfiddle, new version see edit below.)

It's basically doing what andi suggested. For "calculation" I have used UnderscoreJS to find the matching key/value pair for the currently clicked selection with the where method. Then it's easy to get the total of the selection with the length.

In the demo I have done it for male, female and ethnic. It should work exactly like that for the other options.


Edit 07.02.2015

I have improved the code. Now you can activate multiple filters at your list. Also the subtraction is removed because the filtered list holds always the number of items with the length property.

The code is working but there are still some points to improve:

  • removeFilter needs to be changed to easily remove the filter with-out specifing the filter parameter
  • setFilter improvment: if ( index == -1 ) { is not really required because the filters are always removed before adding a new filter.
  • add reset buttons for each radio group. At the moment, there is only a "reset all" button.
  • Using AngularJS for updating the DOM would be better because the script is always recreating the DOM at every filter change.
  • Maybe with AngularJS you could check if there are filters for ng-repeat to do a similar behavior.

It's also likely that I haven't tested if any combination of the filter is working correctly, because I have only three list items. So you have to do more tests with the code.

I updated the code here to the latest version and here is the jsFiddle.


More code explanations 16.02.2015

The functions refreshList and setTotal are used to update the DOM.

The filter functions gender, ethnicity and age are returning the new array after the filter is apply.

The array activeFilters is storing the currently selected filters. It stores the filter functions so they can be called by iterating through this array. More details to this later.

The setFilter method is setting the filter. It adds the filter to the activeFilters array and then it applies the filter.

The applyFilter function is probably the hardest to understand. It iterates through each item of the activeFilters array. If you have two active filters (here gender & ethnic) the array activeFilters will look like this:

activeFilters = [{
   method: function(array, gender){...},
   param: 'male'
   }, {
   method: function (array, ethnic){...},
   param: 'white'
   }];

With activeFilters[0].method(MLAs,'male') you could call the gender filter manually. That's what the $.each loop is doing inside the loop this is {method: ..., param: ...}. So with this.method(filtered, this.param) the filter will be applied to the variable filtered. After the loop every active filter will be applied to the array.

var $total = $('#total');
var $MLA_List = $('#MLA_List');

// MLAs
var MLAs = [{
    "Name": "Nancy Allan",
        "Age": 62,
        "Constuency": "St. Vital",
        "Party": "NDP",
        "Gender": "Female",
        "Ethnicity": "White"
}, {
    "Name": "James Allum",
        "Age": 34,
        "Constuency": "Fort Garry-Riverview",
        "Party": "NDP",
        "Gender": "Male",
        "Ethnicity": "Black"
}, {
    "Name": "Rob Altemeyer",
        "Age": 36,
        "Constuency": "Wolseley",
        "Party": "NDP",
        "Gender": "Male",
        "Ethnicity": "White"
}];

var filteredMLAs = MLAs.slice(0); // copy MLAs
var total = filteredMLAs.length;

var refreshList = function () {
    var list = filteredMLAs;
    setTotal(list.length);

    $MLA_List.empty();
    $.each(list, function (index, value) {
        $MLA_List.append($('<li/>').text(list[index].Name));
    });
};
var setTotal = function (value) {
    $total.text(value);
};

// filter methods
var gender = function (array, gender) {
    //console.log('gender filter called!', gender);
    return _.where(array, {
        "Gender": gender
    });
};

var ethnicity = function (array, ethnic) {
    //console.log('ethnic filter called!', array, ethnic);
    return _.where(array, {
        "Ethnicity": ethnic
    });
};

var age = function(array, ageRange) {
    //under 35, 36-64, 65+
    return _.filter(array, function(MLA) {
        //console.log(MLA.Age);
        switch(ageRange) {
            case 35:
                return ( MLA.Age <= 35 );
            case 36:
                return ( MLA.Age >= 35 && MLA.Age <= 64);
            case 65:
                return ( MLA.Age >= 65 );
        };
        return false;
    });
};

var activeFilters = [];
var setFilter = function (method, param) {
    var newFilter = {
        method: method,
        param: param
    };

    var matchedFilter = _.find(activeFilters, newFilter),
        index = activeFilters.indexOf(matchedFilter);
    
    if ( index == -1 ) {
        activeFilters.push(newFilter);
    } 
    
    applyFilter();
};

var removeFilter = function(method, param) {
    var filter = {
        method: method,
        param: param
    };
    
    var index = activeFilters.indexOf(_.find(activeFilters, filter));
    
    if (index > -1) {
        activeFilters.splice(index, 1);
    }
    
    applyFilter(); // re-apply filter to update list
};

var applyFilter = function () {
    var filtered = MLAs.slice(0);
    $.each(activeFilters, function () {
        filtered = this.method(filtered, this.param);
    });
    filteredMLAs = filtered ? filtered: [];
    refreshList();
};

$('#Male, #Female').click(function () {
    //console.log(this.id);
    removeFilter(gender, this.id=='Male'? 'Female': 'Male'); // remove not active filter
    setFilter(gender, this.id);
});

$('#White, #Black').click(function () {
    //console.log(this.checked);
    if ( this.checked )
        setFilter(ethnicity, this.id); //'White');
    else 
        removeFilter(ethnicity, this.id); //'White');
});

$('.Age').click(function() {
    removeFilter(age, 35); // improvement of remove filter required, e.g. remove all age filters
    removeFilter(age, 36);
    removeFilter(age, 65);
    setFilter(age, parseInt(this.value));
});

$('#reset').click(function(){
    //console.log('reset form');
    activeFilters = [];
    $(':checkbox, :radio').attr('checked', false);
    applyFilter();
});

$(function () {
    refreshList();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
There are <span id="total"></span> MLAs matching.
<ul id="MLA_List"></ul>
<hr/>
<p>click to filter:</p>
<input type="radio" id="Male" name="gender">male</input>
<input type="radio" id="Female" name="gender">female</input>
<input type="checkbox" id="White">white</input>
<input type="checkbox" id="Black">black</input>
<input type="radio" class="Age" name="age" value="35">under 35</input>
<input type="radio" class="Age" name="age" value="36">36-64</input>
<input type="radio" class="Age" name="age" value="65">65+</input>
<button id="reset">reset</button>
<!-- <a href="#" id="male">male</a>
<a href="#" id="female">female</a>
<a href="#" id="white">white</a>
-->

Solution 2:

How about recalculating number each time a category is clicked by looping through every MLA and incrementing a counter? I think you're complicating it by trying to use the previous calculations as a starting point, but it's easier to start fresh every time.


Post a Comment for "Trouble With Math In Javascript, When Elements Clicked"