Skip to content

Commit

Permalink
Support filtering by arbitrary functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
jasondavies committed Mar 18, 2013
1 parent e18c80b commit f0027e7
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 23 deletions.
66 changes: 56 additions & 10 deletions crossfilter.js
Expand Up @@ -569,6 +569,7 @@ function crossfilter() {
filter: filter,
filterExact: filterExact,
filterRange: filterRange,
filterFunction: filterFunction,
filterAll: filterAll,
top: top,
bottom: bottom,
Expand All @@ -585,6 +586,7 @@ function crossfilter() {
newIndex, // temporary array storing newly-added index
sort = quicksort_by(function(i) { return newValues[i]; }),
refilter = crossfilter_filterAll, // for recomputing filter
refilterFunction, // the custom filter function in use
indexListeners = [], // when data is added
dimensionGroups = [],
lo0 = 0,
Expand Down Expand Up @@ -615,9 +617,15 @@ function crossfilter() {
newValues = permute(newValues, newIndex);

// Bisect newValues to determine which new records are selected.
var bounds = refilter(newValues), lo1 = bounds[0], hi1 = bounds[1], i;
for (i = 0; i < lo1; ++i) filters[newIndex[i] + n0] |= one;
for (i = hi1; i < n1; ++i) filters[newIndex[i] + n0] |= one;
var bounds = refilter(newValues), lo1 = bounds[0], hi1 = bounds[1], i, k;
if (refilterFunction) {
for (i = 0; i < n1; ++i) {
if (!refilterFunction(newValues[i], k = newIndex[i] + n0)) filters[k] |= one;
}
} else {
for (i = 0; i < lo1; ++i) filters[newIndex[i] + n0] |= one;
for (i = hi1; i < n1; ++i) filters[newIndex[i] + n0] |= one;
}

// If this dimension previously had no data, then we don't need to do the
// more expensive merge operation; use the new values and index as-is.
Expand Down Expand Up @@ -673,12 +681,21 @@ function crossfilter() {

// Updates the selected values based on the specified bounds [lo, hi].
// This implementation is used by all the public filter methods.
function filterIndex(bounds) {
function filterIndexBounds(bounds) {
var lo1 = bounds[0],
hi1 = bounds[1];

if (refilterFunction) {
refilterFunction = null;
filterIndexFunction(function(d, i) { return lo1 <= i && i < hi1; });
lo0 = lo1;
hi0 = hi1;
return dimension;
}

var i,
j,
k,
lo1 = bounds[0],
hi1 = bounds[1],
added = [],
removed = [];

Expand Down Expand Up @@ -721,24 +738,53 @@ function crossfilter() {
function filter(range) {
return range == null
? filterAll() : Array.isArray(range)
? filterRange(range)
? filterRange(range) : typeof range === "function"
? filterFunction(range)
: filterExact(range);
}

// Filters this dimension to select the exact value.
function filterExact(value) {
return filterIndex((refilter = crossfilter_filterExact(bisect, value))(values));
return filterIndexBounds((refilter = crossfilter_filterExact(bisect, value))(values));
}

// Filters this dimension to select the specified range [lo, hi].
// The lower bound is inclusive, and the upper bound is exclusive.
function filterRange(range) {
return filterIndex((refilter = crossfilter_filterRange(bisect, range))(values));
return filterIndexBounds((refilter = crossfilter_filterRange(bisect, range))(values));
}

// Clears any filters on this dimension.
function filterAll() {
return filterIndex((refilter = crossfilter_filterAll)(values));
return filterIndexBounds((refilter = crossfilter_filterAll)(values));
}

// Filters this dimension using an arbitrary function.
function filterFunction(f) {
refilter = crossfilter_filterAll;

filterIndexFunction(refilterFunction = f);

lo0 = 0;
hi0 = n;

return dimension;
}

function filterIndexFunction(f) {
var i,
k,
x,
added = [],
removed = [];

for (i = 0; i < n; ++i) {
if (!(filters[k = index[i]] & one) ^ (x = f(values[i], k))) {
if (x) filters[k] &= zero, added.push(k);
else filters[k] |= one, removed.push(k);
}
}
filterListeners.forEach(function(l) { l(one, added, removed); });
}

// Returns the top K selected records based on this dimension's order.
Expand Down

0 comments on commit f0027e7

Please sign in to comment.