views:

146

answers:

4

How do I user the JavaScript "filter" attribute as filter my JavaScript object?

I've been reading the following StackOverflow post, and am in a similar situation.

I have the following JavaScript object:

{
'cars' : 
[{
"car_id"          : "1",
"price"           : "42999",
"make_id"         : "050",
"year_built"      : "2007",
"color_id"        : "832"
},
..........
]}

I'm using JQuery to display controls to allow people to filter based on: Price, Make, Year Built, Color

Per that other post, I can use the following code:

// if using an old browser, define the 'filter' attribute
if (!Array.prototype.filter)
{
  Array.prototype.filter = function(fun /*, thisp*/)
  {
    var len = this.length >>> 0;
    if (typeof fun != "function")
      throw new TypeError();

    var res = new Array();
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
      {
        var val = this[i]; // in case fun mutates this
        if (fun.call(thisp, val, i, this))
          res.push(val);
      }
    }

    return res;
  };
}

then to perform the actual filter, I can do:

result = cars.
  filter(function(p) { return p.price >= 15000 }).
  filter(function(p) { return p.price <= 40000 }).
  filter(function(p) { return p.year_built >= 2000 }) etc

What I don't understand is, how do I use my JQuery controls to dynamically change the filter once the filter has already been set? Meaning, let's say I have the filter applied from above, then the user changes there mind and wants to increase the maximum they are willing to pay for a car from $40,000 to $50,000.

How would I problematically modify my filter from :

  filter(function(p) { return p.price <= 40000 }).

to:

  filter(function(p) { return p.price <= 50000 }).
+2  A: 

You could use a function generator.

function createPriceFilter(price)
{
   filter =  function(){ return p.price >= price };
   return filter;
}

Then, when you filter, always use a function generator.

cars
    .filter( createPriceFilter( mySelectedPrice ) )
    .filter( createSomethingFilter(...) )
    . (...)
Stefan Kendall
Maybe I don't understand JavaScript enough but how does this help exactly? Isn't this simply performing the exact same task but now requiring me to create functions instead
PaulR
A: 

Instead of filter, how about a plain old loop:

var min_year = 2000;
var min_price = 15000;
var max_price = 40000;

function fillTable() {
    clearTheTable();
    for (i = 0; i < cars.length; i++) {
        var car = cars[i];
        if (p.price >= min_price && p.price <= max_price && p.year_built >= min_year)
            addCarToTable(car);
    }
}

Each time your parameters change, just call fillTable() again to regenerate the whole table.

(There are much cleverer things you can do but this is the simplest thing I could think of.)

Jason Orendorff
+3  A: 

how do I use my JQuery controls to dynamically change the filter once the filter has already been set?

You don't set a filter. You call filter() with a filter function and get a filtered array back; you can't change the filter that was applied to the array afterwards. Instead you must call filter() again, and pass a different filter function.

Or the same filter function with a closure over a variable that has changed:

var minprice= 10000;
var minpricefilter= function(p) { return p.price>=minprice };
result= cars.filter(minpricefilter);

minprice= 20000;
result= cars.filter(minpricefilter);
bobince
This is a sloppier form of my solution, but the look at the filter reasoning.
Stefan Kendall
A: 

Forget callback based filtering. Enter jOrder: http://github.com/danstocker/jorder.

Filtering by iterating over your entire table is tedious and slow. With jOrder, you search by index:

var table = jOrder(json.cars)
    .index('id', ['car_id'])
    .index('price', ['price'], { grouped: true, ordered: true, type: jOrder.number })
    .index('maker', ['maker_id'], { grouped: true })
    .index('year', ['year_built'], { grouped: true, ordered: true, type: jOrder.number })
    .index('color', ['color_id'], { grouped: true });

Then you get the records you want by:

var filtered = table.where([{ price: { lower: 15000, upper: 40000 } }], { mode: jOrder.range });

Note that you can only apply one inequality filter at a time. To do more, use filtered as an input for a different jOrder table, put only the necessary index on it, and perform the second inequality filter on that one. And so on. Even if you stack up a couple of filters like this, it will be still faster than iteration by a factor of about 10 to 100 depending on the size of your table.

Dan Stocker