Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Multiple arguments to filter result in union of filter operations #22

Open
wants to merge 2 commits into from
@zackham

Addresses issues #13 and #5.

The test says it all:

        data.total.filter([0, 100], 190, [200, 300]);
        assert.isTrue(data.date.top(Infinity).every(function(d) { return (d.total >= 0 && d.total < 100) || (d.total >= 200 && d.total < 300) || d.total == 190; }));
        assert.equal(data.date.top(Infinity).length, 38);

How it works:

  • First range/value is filtered, resulting in only those values selected.
  • State var (union) is set, which disables incremental updates in filterIndex and instead assumes all values in bisected range are not selected, and adds them.
  • Next time filterIndex runs, since hi0/lo0 are no longer valid for incremental updates, a pass is made to select all records and hi0/lo0 are updated accordingly.

Happy to rework this if it isn't coded up the way you'd like, just let me know.

Thanks,
Zack

@jasondavies
Collaborator

Nice!

@zackham

FYI just filled out the contributor agreement, didn't notice it until now.

@baank

Is there someone wrong with this commit ? .. it's a valuable feature and would be great to have in the main Square repo.

@ppong

This looks awesome! will this be merged in anytime soon?

@jofusa

great feature. bump to merge.

@tessenate

Maybe undesired behavior: dim.filter(1) (single argument) returns the dimension object, but dim.filter(1, 2) (multiple arguments) returns the last value.

Also, if you pass in the same value twice (like dim.filter(1, 1)) or any even number of times, it 'cancels out' and the dimension ignores filtering by that dimension.

@jeroenooms

I tried this, and data.total.filter([0, 100], 190, [200, 300]); works as expected, however after adding this filter, the filters on the other dimensions didn't work anymore?

@denzo

What is the status on this? Is there a plan to merge it any time soon?

@RandomEtc
Collaborator

#13 and #5 are currently in the 1.3 Milestone - organized by @jasondavies who has better idea of the dependencies than I do - that puts it a couple of releases off but no big API changes are in the way: https://github.com/square/crossfilter/issues/milestones

We both just took over maintenance of the project last week, so bear with us but things should be moving again soon!

@denzo

That is truly awesome! You guys doing an amazing job!

@KobaKhit

How come this is not yet implemented?! Here is a nice solution in the meantime.

@gsklee

@jasondavies Are we doing anything to this one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 12, 2012
  1. @zackham
Commits on Apr 16, 2012
  1. @zackham
This page is out of date. Refresh to see the latest.
View
84 crossfilter.js
@@ -569,7 +569,9 @@ function crossfilter() {
refilter = crossfilter_filterAll, // for recomputing filter
indexListeners = [], // when data is added
lo0 = 0,
- hi0 = 0;
+ hi0 = 0,
+ union = false, // true when evaluating multiple filters
+ resetNeeded = false; // true after union filter operation (lo0/hi0 invalid)
// Updating a dimension is a two-stage process. First, we must update the
// associated filters for the newly-added records. Once all dimensions have
@@ -660,34 +662,55 @@ function crossfilter() {
added = [],
removed = [];
- // Fast incremental update based on previous lo index.
- if (lo1 < lo0) {
- for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) {
- filters[k = index[i]] ^= one;
- added.push(k);
- }
- } else if (lo1 > lo0) {
- for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) {
- filters[k = index[i]] ^= one;
- removed.push(k);
+ if(resetNeeded) {
+ // lo0/hi0 invalid - select all and reset
+ for(i = 0; i < n; ++i) {
+ if(filters[k = index[i]] & one) {
+ filters[k] ^= one;
+ added.push(k);
+ }
}
+ lo0 = 0;
+ hi0 = values.length;
+ resetNeeded = false;
}
-
- // Fast incremental update based on previous hi index.
- if (hi1 > hi0) {
- for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) {
+ if(union) {
+ for(i = lo1, j = hi1; i < j; ++i) {
filters[k = index[i]] ^= one;
added.push(k);
}
- } else if (hi1 < hi0) {
- for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) {
- filters[k = index[i]] ^= one;
- removed.push(k);
+ // expand hi0/lo0 range
+ if(lo0 > lo1) lo0 = lo1;
+ if(hi0 < hi1) hi0 = hi1;
+ } else {
+ // Fast incremental update based on previous lo index.
+ if (lo1 < lo0) {
+ for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) {
+ filters[k = index[i]] ^= one;
+ added.push(k);
+ }
+ } else if (lo1 > lo0) {
+ for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) {
+ filters[k = index[i]] ^= one;
+ removed.push(k);
+ }
}
- }
- lo0 = lo1;
- hi0 = hi1;
+ // Fast incremental update based on previous hi index.
+ if (hi1 > hi0) {
+ for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) {
+ filters[k = index[i]] ^= one;
+ added.push(k);
+ }
+ } else if (hi1 < hi0) {
+ for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) {
+ filters[k = index[i]] ^= one;
+ removed.push(k);
+ }
+ }
+ lo0 = lo1;
+ hi0 = hi1;
+ }
filterListeners.forEach(function(l) { l(one, added, removed); });
return dimension;
}
@@ -697,10 +720,25 @@ function crossfilter() {
// If the range is an array, this is equivalent to filterRange.
// Otherwise, this is equivalent to filterExact.
function filter(range) {
- return range == null
+ if(arguments.length > 1) {
+ var result,i,len;
+ for(i=0,len=arguments.length; i < len; ++i) {
+ if(i==1) union=true;
+ range = arguments[i];
+ if(Array.isArray(range))
+ filterRange(range);
+ else
+ filterExact(range);
+ }
+ union = false;
+ resetNeeded = true;
+ return arguments[len-1];
+ } else {
+ return range == null
? filterAll() : Array.isArray(range)
? filterRange(range)
: filterExact(range);
+ }
}
// Filters this dimension to select the exact value.
View
2  crossfilter.min.js
@@ -1 +1 @@
-(function(a){function b(a){return a}function c(a,b){for(var c=0,d=b.length,e=new Array(d);c<d;++c)e[c]=a[b[c]];return e}function e(a){function b(b,c,d,e){while(d<e){var f=d+e>>1;a(b[f])<c?d=f+1:e=f}return d}function c(b,c,d,e){while(d<e){var f=d+e>>1;c<a(b[f])?e=f:d=f+1}return d}return c.right=c,c.left=b,c}function h(a){function b(a,b,c){var e=c-b,f=(e>>>1)+1;while(--f>0)d(a,f,e,b);return a}function c(a,b,c){var e=c-b,f;while(--e>0)f=a[b],a[b]=a[b+e],a[b+e]=f,d(a,1,e,b);return a}function d(b,c,d,e){var f=b[--e+c],g=a(f),h;while((h=c<<1)<=d){h<d&&a(b[e+h])>a(b[e+h+1])&&h++;if(g<=a(b[e+h]))break;b[e+c]=b[e+h],c=h}b[e+c]=f}return b.sort=c,b}function j(a){function c(c,d,e,f){var g=new Array(f=Math.min(e-d,f)),h,i,j,k;for(i=0;i<f;++i)g[i]=c[d++];b(g,0,f);if(d<e){h=a(g[0]);do if(j=a(k=c[d])>h)g[0]=k,h=a(b(g,0,f)[0]);while(++d<e)}return g}var b=h(a);return c}function l(a){function b(b,c,d){for(var e=c+1;e<d;++e){for(var f=e,g=b[e],h=a(g);f>c&&a(b[f-1])>h;--f)b[f]=b[f-1];b[f]=g}return b}return b}function n(a){function c(a,c,e){return(e-c<o?b:d)(a,c,e)}function d(b,d,e){var f=(e-d)/6|0,g=d+f,h=e-1-f,i=d+e-1>>1,j=i-f,k=i+f,l=b[g],m=a(l),n=b[j],o=a(n),p=b[i],q=a(p),r=b[k],s=a(r),u=b[h],v=a(u);m>o&&(t=l,l=n,n=t,t=m,m=o,o=t),s>v&&(t=r,r=u,u=t,t=s,s=v,v=t),m>q&&(t=l,l=p,p=t,t=m,m=q,q=t),o>q&&(t=n,n=p,p=t,t=o,o=q,q=t),m>s&&(t=l,l=r,r=t,t=m,m=s,s=t),q>s&&(t=p,p=r,r=t,t=q,q=s,s=t),o>v&&(t=n,n=u,u=t,t=o,o=v,v=t),o>q&&(t=n,n=p,p=t,t=o,o=q,q=t),s>v&&(t=r,r=u,u=t,t=s,s=v,v=t);var w=n,x=o,y=r,z=s;b[g]=l,b[j]=b[d],b[i]=p,b[k]=b[e-1],b[h]=u;var A=d+1,B=e-2,C=x<=z&&x>=z;if(C)for(var D=A;D<=B;++D){var E=b[D],F=a(E);if(F<x)D!==A&&(b[D]=b[A],b[A]=E),++A;else if(F>x)for(;;){var G=a(b[B]);if(G>x){B--;continue}if(G<x){b[D]=b[A],b[A++]=b[B],b[B--]=E;break}b[D]=b[B],b[B--]=E;break}}else for(var D=A;D<=B;D++){var E=b[D],F=a(E);if(F<x)D!==A&&(b[D]=b[A],b[A]=E),++A;else if(F>z)for(;;){var G=a(b[B]);if(G>z){B--;if(B<D)break;continue}G<x?(b[D]=b[A],b[A++]=b[B],b[B--]=E):(b[D]=b[B],b[B--]=E);break}}b[d]=b[A-1],b[A-1]=w,b[e-1]=b[B+1],b[B+1]=y,c(b,d,A-1),c(b,B+2,e);if(C)return b;if(A<g&&B>h){var H,G;while((H=a(b[A]))<=x&&H>=x)++A;while((G=a(b[B]))<=z&&G>=z)--B;for(var D=A;D<=B;D++){var E=b[D],F=a(E);if(F<=x&&F>=x)D!==A&&(b[D]=b[A],b[A]=E),A++;else if(F<=z&&F>=z)for(;;){var G=a(b[B]);if(G<=z&&G>=z){B--;if(B<D)break;continue}G<x?(b[D]=b[A],b[A++]=b[B],b[B--]=E):(b[D]=b[B],b[B--]=E);break}}}return c(b,A,B+1)}var b=l(a);return c}function v(a){return new Array(a)}function w(a,b){return function(c){var d=c.length;return[a.left(c,b,0,d),a.right(c,b,0,d)]}}function x(a,b){var c=b[0],d=b[1];return function(b){var e=b.length;return[a.left(b,c,0,e),a.left(b,d,0,e)]}}function y(a){return[0,a.length]}function z(){return null}function A(){return 0}function B(a){return a+1}function C(a){return a-1}function D(a){return function(b,c){return b+ +a(c)}}function E(a){return function(b,c){return b-a(c)}}function F(){function q(b){var c=f,d=b.length;return d&&(e=e.concat(b),l=s(l,f+=d),o.forEach(function(a){a(b,c,d)})),a}function r(a){function Q(b,d,e){F=b.map(a),J=K(H(e),0,e),F=c(F,J);var g=L(F),h=g[0],i=g[1],j;for(j=0;j<h;++j)l[J[j]+d]|=q;for(j=i;j<e;++j)l[J[j]+d]|=q;if(!d){t=F,v=J,O=h,P=i;return}var k=t,m=v,n=0,o=0;t=new Array(f),v=G(f,f);for(j=0;n<d&&o<e;++j)k[n]<F[o]?(t[j]=k[n],v[j]=m[n++]):(t[j]=F[o],v[j]=J[o++]+d);for(;n<d;++n,++j)t[j]=k[n],v[j]=m[n];for(;o<e;++o,++j)t[j]=F[o],v[j]=J[o]+d;g=L(t),O=g[0],P=g[1]}function R(a,b,c){N.forEach(function(a){a(F,J,b,c)}),F=J=null}function S(a){var b,c,d,e=a[0],f=a[1],g=[],h=[];if(e<O)for(b=e,c=Math.min(O,f);b<c;++b)l[d=v[b]]^=q,g.push(d);else if(e>O)for(b=O,c=Math.min(e,P);b<c;++b)l[d=v[b]]^=q,h.push(d);if(f>P)for(b=Math.max(e,P),c=f;b<c;++b)l[d=v[b]]^=q,g.push(d);else if(f<P)for(b=Math.max(O,f),c=P;b<c;++b)l[d=v[b]]^=q,h.push(d);return O=e,P=f,m.forEach(function(a){a(q,g,h)}),p}function T(a){return a==null?W():Array.isArray(a)?V(a):U(a)}function U(a){return S((L=w(d,a))(t))}function V(a){return S((L=x(d,a))(t))}function W(){return S((L=y)(t))}function X(a){var b=[],c=P,d;while(--c>=O&&a>0)l[d=v[c]]||(b.push(e[d]),--a);return b}function Y(a){function L(b,c,g,h){function N(){++o===n&&(p=u(p,k<<=1),i=u(i,k),n=I(k))}var j=d,p=G(o,n),q=x,t=F,v=o,w=0,y=0,A,B,C,D,E,L;K&&(q=t=z),d=new Array(o),o=0,i=v>1?s(i,f):G(f,n),v&&(C=(B=j[0]).key);while(y<h&&!((D=a(b[y]))>=D))++y;while(y<h){if(B&&C<=D){E=B,L=C,p[w]=o;if(B=j[++w])C=B.key}else E={key:D,value:t()},L=D;d[o]=E;while(!(D>L)){i[A=c[y]+g]=o,l[A]&r||(E.value=q(E.value,e[A]));if(++y>=h)break;D=a(b[y])}N()}while(w<v)d[p[w]=o]=j[w++],N();if(o>w)for(w=0;w<g;++w)i[w]=p[i[w]];A=m.indexOf(H),o>1?(H=M,J=P):(o===1?(H=O,J=Q):(H=z,J=z),i=null),m[A]=H}function M(a,b,c){if(a===q||K)return;var f,h,j;for(f=0,j=b.length;f<j;++f)l[h=b[f]]&r||(g=d[i[h]],g.value=x(g.value,e[h]));for(f=0,j=c.length;f<j;++f)(l[h=c[f]]&r)===a&&(g=d[i[h]],g.value=y(g.value,e[h]))}function O(a,b,c){if(a===q||K)return;var f,g,h,i=d[0];for(f=0,h=b.length;f<h;++f)l[g=b[f]]&r||(i.value=x(i.value,e[g]));for(f=0,h=c.length;f<h;++f)(l[g=c[f]]&r)===a&&(i.value=y(i.value,e[g]))}function P(){var a,b;for(a=0;a<o;++a)d[a].value=F();for(a=0;a<f;++a)l[a]&r||(b=d[i[a]],b.value=x(b.value,e[a]))}function Q(){var a,b=d[0];b.value=F();for(a=0;a<f;++a)l[a]&r||(b.value=x(b.value,e[a]))}function R(){return K&&(J(),K=!1),d}function S(a){var b=p(R(),0,d.length,a);return w.sort(b,0,b.length)}function T(a,b,d){return x=a,y=b,F=d,K=!0,c}function U(){return T(B,C,A)}function V(a){return T(D(a),E(a),A)}function W(a){function b(b){return a(b.value)}return p=j(b),w=h(b),c}function X(){return W(b)}function Y(){return o}var c={top:S,all:R,reduce:T,reduceCount:U,reduceSum:V,order:W,orderNatural:X,size:Y},d,i,k=8,n=I(k),o=0,p,w,x,y,F,H=z,J=z,K=!0;return arguments.length<1&&(a=b),m.push(H),N.push(L),L(t,v,0,f),U().orderNatural()}function Z(){var a=Y(z),b=a.all;return delete a.all,delete a.top,delete a.order,delete a.orderNatural,delete a.size,a.value=function(){return b()[0].value},a}var p={filter:T,filterExact:U,filterRange:V,filterAll:W,top:X,group:Y,groupAll:Z},q=1<<i++,r=~q,t,v,F,J,K=n(function(a){return F[a]}),L=y,N=[],O=0,P=0;return o.unshift(Q),o.push(R),i>k&&(l=u(l,k<<=1)),Q(e,0,f),R(e,0,f),p}function t(){function i(a,d,g){var i;if(h)return;for(i=d;i<f;++i)l[i]||(b=c(b,e[i]))}function j(a,f,g){var i,j,k;if(h)return;for(i=0,k=f.length;i<k;++i)l[j=f[i]]||(b=c(b,e[j]));for(i=0,k=g.length;i<k;++i)l[j=g[i]]===a&&(b=d(b,e[j]))}function k(){var a;b=g();for(a=0;a<f;++a)l[a]||(b=c(b,e[a]))}function n(b,e,f){return c=b,d=e,g=f,h=!0,a}function p(){return n(B,C,A)}function q(a){return n(D(a),E(a),A)}function r(){return h&&(k(),h=!1),b}var a={reduce:n,reduceCount:p,reduceSum:q,value:r},b,c,d,g,h=!0;return m.push(j),o.push(i),i(e,0,f),p()}function v(){return f}var a={add:q,dimension:r,groupAll:t,size:v},e=[],f=0,i=0,k=8,l=p(0),m=[],o=[];return arguments.length?q(arguments[0]):a}function G(a,b){return(b<257?p:b<65537?q:r)(a)}function H(a){var b=G(a,a);for(var c=-1;++c<a;)b[c]=c;return b}function I(a){return a===8?256:a===16?65536:4294967296}F.version="1.0.2",F.permute=c;var d=F.bisect=e(b);d.by=e;var f=F.heap=h(b);f.by=h;var i=F.heapselect=j(b);i.by=j;var k=F.insertionsort=l(b);k.by=l;var m=F.quicksort=n(b);m.by=n;var o=32,p=v,q=v,r=v,s=b,u=b;typeof Uint8Array!="undefined"&&(p=function(a){return new Uint8Array(a)},q=function(a){return new Uint16Array(a)},r=function(a){return new Uint32Array(a)},s=function(a,b){var c=new a.constructor(b);return c.set(a),c},u=function(a,b){var c;switch(b){case 16:c=q(a.length);break;case 32:c=r(a.length);break;default:throw new Error("invalid array width!")}return c.set(a),c}),a.crossfilter=F})(this);
+(function(a){function b(a){return a}function c(a,b){for(var c=0,d=b.length,e=new Array(d);c<d;++c)e[c]=a[b[c]];return e}function e(a){function b(b,c,d,e){while(d<e){var f=d+e>>1;a(b[f])<c?d=f+1:e=f}return d}function c(b,c,d,e){while(d<e){var f=d+e>>1;c<a(b[f])?e=f:d=f+1}return d}return c.right=c,c.left=b,c}function h(a){function b(a,b,c){var e=c-b,f=(e>>>1)+1;while(--f>0)d(a,f,e,b);return a}function c(a,b,c){var e=c-b,f;while(--e>0)f=a[b],a[b]=a[b+e],a[b+e]=f,d(a,1,e,b);return a}function d(b,c,d,e){var f=b[--e+c],g=a(f),h;while((h=c<<1)<=d){h<d&&a(b[e+h])>a(b[e+h+1])&&h++;if(g<=a(b[e+h]))break;b[e+c]=b[e+h],c=h}b[e+c]=f}return b.sort=c,b}function j(a){function c(c,d,e,f){var g=new Array(f=Math.min(e-d,f)),h,i,j,k;for(i=0;i<f;++i)g[i]=c[d++];b(g,0,f);if(d<e){h=a(g[0]);do if(j=a(k=c[d])>h)g[0]=k,h=a(b(g,0,f)[0]);while(++d<e)}return g}var b=h(a);return c}function l(a){function b(b,c,d){for(var e=c+1;e<d;++e){for(var f=e,g=b[e],h=a(g);f>c&&a(b[f-1])>h;--f)b[f]=b[f-1];b[f]=g}return b}return b}function n(a){function c(a,c,e){return(e-c<o?b:d)(a,c,e)}function d(b,d,e){var f=(e-d)/6|0,g=d+f,h=e-1-f,i=d+e-1>>1,j=i-f,k=i+f,l=b[g],m=a(l),n=b[j],o=a(n),p=b[i],q=a(p),r=b[k],s=a(r),u=b[h],v=a(u);m>o&&(t=l,l=n,n=t,t=m,m=o,o=t),s>v&&(t=r,r=u,u=t,t=s,s=v,v=t),m>q&&(t=l,l=p,p=t,t=m,m=q,q=t),o>q&&(t=n,n=p,p=t,t=o,o=q,q=t),m>s&&(t=l,l=r,r=t,t=m,m=s,s=t),q>s&&(t=p,p=r,r=t,t=q,q=s,s=t),o>v&&(t=n,n=u,u=t,t=o,o=v,v=t),o>q&&(t=n,n=p,p=t,t=o,o=q,q=t),s>v&&(t=r,r=u,u=t,t=s,s=v,v=t);var w=n,x=o,y=r,z=s;b[g]=l,b[j]=b[d],b[i]=p,b[k]=b[e-1],b[h]=u;var A=d+1,B=e-2,C=x<=z&&x>=z;if(C)for(var D=A;D<=B;++D){var E=b[D],F=a(E);if(F<x)D!==A&&(b[D]=b[A],b[A]=E),++A;else if(F>x)for(;;){var G=a(b[B]);if(G>x){B--;continue}if(G<x){b[D]=b[A],b[A++]=b[B],b[B--]=E;break}b[D]=b[B],b[B--]=E;break}}else for(var D=A;D<=B;D++){var E=b[D],F=a(E);if(F<x)D!==A&&(b[D]=b[A],b[A]=E),++A;else if(F>z)for(;;){var G=a(b[B]);if(G>z){B--;if(B<D)break;continue}G<x?(b[D]=b[A],b[A++]=b[B],b[B--]=E):(b[D]=b[B],b[B--]=E);break}}b[d]=b[A-1],b[A-1]=w,b[e-1]=b[B+1],b[B+1]=y,c(b,d,A-1),c(b,B+2,e);if(C)return b;if(A<g&&B>h){var H,G;while((H=a(b[A]))<=x&&H>=x)++A;while((G=a(b[B]))<=z&&G>=z)--B;for(var D=A;D<=B;D++){var E=b[D],F=a(E);if(F<=x&&F>=x)D!==A&&(b[D]=b[A],b[A]=E),A++;else if(F<=z&&F>=z)for(;;){var G=a(b[B]);if(G<=z&&G>=z){B--;if(B<D)break;continue}G<x?(b[D]=b[A],b[A++]=b[B],b[B--]=E):(b[D]=b[B],b[B--]=E);break}}}return c(b,A,B+1)}var b=l(a);return c}function v(a){return new Array(a)}function w(a,b){return function(c){var d=c.length;return[a.left(c,b,0,d),a.right(c,b,0,d)]}}function x(a,b){var c=b[0],d=b[1];return function(b){var e=b.length;return[a.left(b,c,0,e),a.left(b,d,0,e)]}}function y(a){return[0,a.length]}function z(){return null}function A(){return 0}function B(a){return a+1}function C(a){return a-1}function D(a){return function(b,c){return b+ +a(c)}}function E(a){return function(b,c){return b-a(c)}}function F(){function q(b){var c=f,d=b.length;return d&&(e=e.concat(b),l=s(l,f+=d),o.forEach(function(a){a(b,c,d)})),a}function r(a){function S(b,d,e){F=b.map(a),J=K(H(e),0,e),F=c(F,J);var g=L(F),h=g[0],i=g[1],j;for(j=0;j<h;++j)l[J[j]+d]|=q;for(j=i;j<e;++j)l[J[j]+d]|=q;if(!d){t=F,v=J,O=h,P=i;return}var k=t,m=v,n=0,o=0;t=new Array(f),v=G(f,f);for(j=0;n<d&&o<e;++j)k[n]<F[o]?(t[j]=k[n],v[j]=m[n++]):(t[j]=F[o],v[j]=J[o++]+d);for(;n<d;++n,++j)t[j]=k[n],v[j]=m[n];for(;o<e;++o,++j)t[j]=F[o],v[j]=J[o]+d;g=L(t),O=g[0],P=g[1]}function T(a,b,c){N.forEach(function(a){a(F,J,b,c)}),F=J=null}function U(a){var b,c,d,e=a[0],g=a[1],h=[],i=[];if(R){for(b=0;b<f;++b)l[d=v[b]]&q&&(l[d]^=q,h.push(d));O=0,P=t.length,R=!1}if(Q){for(b=e,c=g;b<c;++b)l[d=v[b]]^=q,h.push(d);O>e&&(O=e),P<g&&(P=g)}else{if(e<O)for(b=e,c=Math.min(O,g);b<c;++b)l[d=v[b]]^=q,h.push(d);else if(e>O)for(b=O,c=Math.min(e,P);b<c;++b)l[d=v[b]]^=q,i.push(d);if(g>P)for(b=Math.max(e,P),c=g;b<c;++b)l[d=v[b]]^=q,h.push(d);else if(g<P)for(b=Math.max(O,g),c=P;b<c;++b)l[d=v[b]]^=q,i.push(d);O=e,P=g}return m.forEach(function(a){a(q,h,i)}),p}function V(a){if(arguments.length>1){var b,c,d;for(c=0,d=arguments.length;c<d;++c)c==1&&(Q=!0),a=arguments[c],Array.isArray(a)?X(a):W(a);return Q=!1,R=!0,arguments[d-1]}return a==null?Y():Array.isArray(a)?X(a):W(a)}function W(a){return U((L=w(d,a))(t))}function X(a){return U((L=x(d,a))(t))}function Y(){return U((L=y)(t))}function Z(a){var b=[],c=P,d;while(--c>=O&&a>0)l[d=v[c]]||(b.push(e[d]),--a);return b}function $(a){function L(b,c,g,h){function N(){++o===n&&(p=u(p,k<<=1),i=u(i,k),n=I(k))}var j=d,p=G(o,n),q=x,t=F,v=o,w=0,y=0,A,B,C,D,E,L;K&&(q=t=z),d=new Array(o),o=0,i=v>1?s(i,f):G(f,n),v&&(C=(B=j[0]).key);while(y<h&&!((D=a(b[y]))>=D))++y;while(y<h){if(B&&C<=D){E=B,L=C,p[w]=o;if(B=j[++w])C=B.key}else E={key:D,value:t()},L=D;d[o]=E;while(!(D>L)){i[A=c[y]+g]=o,l[A]&r||(E.value=q(E.value,e[A]));if(++y>=h)break;D=a(b[y])}N()}while(w<v)d[p[w]=o]=j[w++],N();if(o>w)for(w=0;w<g;++w)i[w]=p[i[w]];A=m.indexOf(H),o>1?(H=M,J=P):(o===1?(H=O,J=Q):(H=z,J=z),i=null),m[A]=H}function M(a,b,c){if(a===q||K)return;var f,h,j;for(f=0,j=b.length;f<j;++f)l[h=b[f]]&r||(g=d[i[h]],g.value=x(g.value,e[h]));for(f=0,j=c.length;f<j;++f)(l[h=c[f]]&r)===a&&(g=d[i[h]],g.value=y(g.value,e[h]))}function O(a,b,c){if(a===q||K)return;var f,g,h,i=d[0];for(f=0,h=b.length;f<h;++f)l[g=b[f]]&r||(i.value=x(i.value,e[g]));for(f=0,h=c.length;f<h;++f)(l[g=c[f]]&r)===a&&(i.value=y(i.value,e[g]))}function P(){var a,b;for(a=0;a<o;++a)d[a].value=F();for(a=0;a<f;++a)l[a]&r||(b=d[i[a]],b.value=x(b.value,e[a]))}function Q(){var a,b=d[0];b.value=F();for(a=0;a<f;++a)l[a]&r||(b.value=x(b.value,e[a]))}function R(){return K&&(J(),K=!1),d}function S(a){var b=p(R(),0,d.length,a);return w.sort(b,0,b.length)}function T(a,b,d){return x=a,y=b,F=d,K=!0,c}function U(){return T(B,C,A)}function V(a){return T(D(a),E(a),A)}function W(a){function b(b){return a(b.value)}return p=j(b),w=h(b),c}function X(){return W(b)}function Y(){return o}var c={top:S,all:R,reduce:T,reduceCount:U,reduceSum:V,order:W,orderNatural:X,size:Y},d,i,k=8,n=I(k),o=0,p,w,x,y,F,H=z,J=z,K=!0;return arguments.length<1&&(a=b),m.push(H),N.push(L),L(t,v,0,f),U().orderNatural()}function _(){var a=$(z),b=a.all;return delete a.all,delete a.top,delete a.order,delete a.orderNatural,delete a.size,a.value=function(){return b()[0].value},a}var p={filter:V,filterExact:W,filterRange:X,filterAll:Y,top:Z,group:$,groupAll:_},q=1<<i++,r=~q,t,v,F,J,K=n(function(a){return F[a]}),L=y,N=[],O=0,P=0,Q=!1,R=!1;return o.unshift(S),o.push(T),i>k&&(l=u(l,k<<=1)),S(e,0,f),T(e,0,f),p}function t(){function i(a,d,g){var i;if(h)return;for(i=d;i<f;++i)l[i]||(b=c(b,e[i]))}function j(a,f,g){var i,j,k;if(h)return;for(i=0,k=f.length;i<k;++i)l[j=f[i]]||(b=c(b,e[j]));for(i=0,k=g.length;i<k;++i)l[j=g[i]]===a&&(b=d(b,e[j]))}function k(){var a;b=g();for(a=0;a<f;++a)l[a]||(b=c(b,e[a]))}function n(b,e,f){return c=b,d=e,g=f,h=!0,a}function p(){return n(B,C,A)}function q(a){return n(D(a),E(a),A)}function r(){return h&&(k(),h=!1),b}var a={reduce:n,reduceCount:p,reduceSum:q,value:r},b,c,d,g,h=!0;return m.push(j),o.push(i),i(e,0,f),p()}function v(){return f}var a={add:q,dimension:r,groupAll:t,size:v},e=[],f=0,i=0,k=8,l=p(0),m=[],o=[];return arguments.length?q(arguments[0]):a}function G(a,b){return(b<257?p:b<65537?q:r)(a)}function H(a){var b=G(a,a);for(var c=-1;++c<a;)b[c]=c;return b}function I(a){return a===8?256:a===16?65536:4294967296}F.version="1.0.2",F.permute=c;var d=F.bisect=e(b);d.by=e;var f=F.heap=h(b);f.by=h;var i=F.heapselect=j(b);i.by=j;var k=F.insertionsort=l(b);k.by=l;var m=F.quicksort=n(b);m.by=n;var o=32,p=v,q=v,r=v,s=b,u=b;typeof Uint8Array!="undefined"&&(p=function(a){return new Uint8Array(a)},q=function(a){return new Uint16Array(a)},r=function(a){return new Uint32Array(a)},s=function(a,b){var c=new a.constructor(b);return c.set(a),c},u=function(a,b){var c;switch(b){case 16:c=q(a.length);break;case 32:c=r(a.length);break;default:throw new Error("invalid array width!")}return c.set(a),c}),a.crossfilter=F})(this);
View
84 src/crossfilter.js
@@ -56,7 +56,9 @@ function crossfilter() {
refilter = crossfilter_filterAll, // for recomputing filter
indexListeners = [], // when data is added
lo0 = 0,
- hi0 = 0;
+ hi0 = 0,
+ union = false, // true when evaluating multiple filters
+ resetNeeded = false; // true after union filter operation (lo0/hi0 invalid)
// Updating a dimension is a two-stage process. First, we must update the
// associated filters for the newly-added records. Once all dimensions have
@@ -147,34 +149,55 @@ function crossfilter() {
added = [],
removed = [];
- // Fast incremental update based on previous lo index.
- if (lo1 < lo0) {
- for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) {
- filters[k = index[i]] ^= one;
- added.push(k);
- }
- } else if (lo1 > lo0) {
- for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) {
- filters[k = index[i]] ^= one;
- removed.push(k);
+ if(resetNeeded) {
+ // lo0/hi0 invalid - select all and reset
+ for(i = 0; i < n; ++i) {
+ if(filters[k = index[i]] & one) {
+ filters[k] ^= one;
+ added.push(k);
+ }
}
+ lo0 = 0;
+ hi0 = values.length;
+ resetNeeded = false;
}
-
- // Fast incremental update based on previous hi index.
- if (hi1 > hi0) {
- for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) {
+ if(union) {
+ for(i = lo1, j = hi1; i < j; ++i) {
filters[k = index[i]] ^= one;
added.push(k);
}
- } else if (hi1 < hi0) {
- for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) {
- filters[k = index[i]] ^= one;
- removed.push(k);
+ // expand hi0/lo0 range
+ if(lo0 > lo1) lo0 = lo1;
+ if(hi0 < hi1) hi0 = hi1;
+ } else {
+ // Fast incremental update based on previous lo index.
+ if (lo1 < lo0) {
+ for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) {
+ filters[k = index[i]] ^= one;
+ added.push(k);
+ }
+ } else if (lo1 > lo0) {
+ for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) {
+ filters[k = index[i]] ^= one;
+ removed.push(k);
+ }
}
- }
- lo0 = lo1;
- hi0 = hi1;
+ // Fast incremental update based on previous hi index.
+ if (hi1 > hi0) {
+ for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) {
+ filters[k = index[i]] ^= one;
+ added.push(k);
+ }
+ } else if (hi1 < hi0) {
+ for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) {
+ filters[k = index[i]] ^= one;
+ removed.push(k);
+ }
+ }
+ lo0 = lo1;
+ hi0 = hi1;
+ }
filterListeners.forEach(function(l) { l(one, added, removed); });
return dimension;
}
@@ -184,10 +207,25 @@ function crossfilter() {
// If the range is an array, this is equivalent to filterRange.
// Otherwise, this is equivalent to filterExact.
function filter(range) {
- return range == null
+ if(arguments.length > 1) {
+ var result,i,len;
+ for(i=0,len=arguments.length; i < len; ++i) {
+ if(i==1) union=true;
+ range = arguments[i];
+ if(Array.isArray(range))
+ filterRange(range);
+ else
+ filterExact(range);
+ }
+ union = false;
+ resetNeeded = true;
+ return arguments[len-1];
+ } else {
+ return range == null
? filterAll() : Array.isArray(range)
? filterRange(range)
: filterExact(range);
+ }
}
// Filters this dimension to select the exact value.
View
9 test/crossfilter-test.js
@@ -244,6 +244,15 @@ suite.addBatch({
assert.lesser(data.date.top(Infinity).length, 43);
data.total.filter(null);
assert.equal(data.date.top(Infinity).length, 43);
+ },
+ "can be passed multiple arguments and returns union of filters": function(data) {
+ try {
+ data.total.filter([0, 100], 190, [200, 300]);
+ assert.isTrue(data.total.top(Infinity).every(function(d) { return (d.total >= 0 && d.total < 100) || (d.total >= 200 && d.total < 300) || d.total == 190; }));
+ assert.equal(data.total.top(Infinity).length, 38);
+ } finally {
+ data.total.filterAll();
+ }
}
},
Something went wrong with that request. Please try again.