Skip to content

Commit 9d07086

Browse files
Macy Abbeygordonwoodhull
Macy Abbey
authored andcommitted
eliminate group.top in cap mixin
the ordering function. This produced really strange results because of how the top function works. It uses crossfilter.group.all(), and then takes the last N elements as the top. The order of all by default is ascending value by default. The order of dcjs's ordering function is ascending key by default. So if you left everything default, you would get fairly expected results. The largest value groups, sorted by key. But if you use your own ordering function, you get very strange results, because your ordering function does not take affect until the biggest set has already been determined by group.all().top(). Added a new test which fails with current capping logic because of this problem. Changed implementation of data function in cap which resolves this new test. Changed existing tests which tested for incorrect results that used to be produced.
1 parent 3326423 commit 9d07086

File tree

2 files changed

+125
-8
lines changed

2 files changed

+125
-8
lines changed

spec/pie-chart-spec.js

+98-7
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,24 @@ describe('dc.pieChart', function () {
6464
return chart;
6565
}
6666

67+
function buildCountryChart (id) {
68+
var div = appendChartID(id);
69+
div.append('a').attr('class', 'reset').style('display', 'none');
70+
div.append('span').attr('class', 'filter').style('display', 'none');
71+
var chart = dc.pieChart('#' + id);
72+
chart.dimension(countryDimension).group(countryGroup)
73+
.width(width)
74+
.height(height)
75+
.radius(radius)
76+
.transitionDuration(0);
77+
chart.render();
78+
return chart;
79+
}
80+
6781
describe('generation', function () {
68-
var chart;
82+
var chart,
83+
countryChart;
84+
6985
beforeEach(function () {
7086
chart = buildChart('pie-chart-age');
7187
chart.innerRadius(innerRadius);
@@ -330,10 +346,72 @@ describe('dc.pieChart', function () {
330346
beforeEach(function () {
331347
chart.cap(4).ordering(dc.pluck('value'));
332348
});
333-
it('group should be orderd', function () {
334-
expect(['33','55','22','44','Others']).toEqual(chart.data().map(dc.pluck('key')));
349+
it('group should be ordered', function () {
350+
//Value ordered by value dimension, which is just a count.
351+
//Collissions at count of 2 resolved by order initial dataset.
352+
// 44 -> 3
353+
// 22 -> 2
354+
// 33 -> 2
355+
// 55 -> 2
356+
// 66 -> 1
357+
expect(['22','33','55','44','Others']).toEqual(chart.data().map(dc.pluck('key')));
358+
359+
//Value dimension ordered by key
335360
chart.ordering(dc.pluck('key'));
336-
expect(['22','33','44','55','Others']).toEqual(chart.data().map(dc.pluck('key')));
361+
362+
expect(['33','44','55','66','Others']).toEqual(chart.data().map(dc.pluck('key')));
363+
});
364+
afterEach(function () {
365+
chart.cap(Infinity).ordering(dc.pluck('key'));
366+
});
367+
});
368+
describe('group order when ordering is different than crossfilter group ordering', function () {
369+
var crossfilterOrder,
370+
crossfilterTop1,
371+
chartOrder,
372+
defaultCapOrder,
373+
reverseValueOrder,
374+
valueOrder;
375+
376+
beforeEach(function () {
377+
countryChart = buildCountryChart('country-chart');
378+
countryChart.innerRadius(innerRadius);
379+
380+
//An array sorted in ascending value order.
381+
//[{"key":"CA","value":2},{"key":"US","value":8}]
382+
crossfilterOrder = countryGroup.all();
383+
384+
//This would be {"key":"US","value":8},
385+
//top function takes elements from the all array, last to first.
386+
crossfilterTop1 = countryGroup.top(1);
387+
388+
//["CA", "US"] no ordering applied, top not used, so uses order of all.
389+
chartOrder = countryChart.data().map(dc.pluck('key'));
390+
391+
//Once you cap the chart, this forces the ordering function to sort.
392+
//By default that should be by key, since the default ordering is dc.pluck('key')
393+
countryChart.cap(1);
394+
395+
//This will be US, Others. US is higher by key and by value (8)
396+
defaultCapOrder = countryChart.data().map(dc.pluck('key'));
397+
398+
countryChart.ordering(function (d) {
399+
return -d.value;
400+
});
401+
402+
//This should be ["CA", "Others"]. CA is now higher because it is -2 and US is -8.
403+
reverseValueOrder = countryChart.data().map(dc.pluck('key'));
404+
405+
countryChart.render();
406+
407+
});
408+
it('group should be ordered when dimension key and ordering are different orders, by ordering.', function () {
409+
expect(['CA','Others']).toEqual(reverseValueOrder);
410+
countryChart.ordering(function (d) {
411+
return d.value;
412+
});
413+
valueOrder = countryChart.data().map(dc.pluck('key'));
414+
expect(['US','Others']).toEqual(valueOrder);
337415
});
338416
afterEach(function () {
339417
chart.cap(Infinity).ordering(dc.pluck('key'));
@@ -441,14 +519,27 @@ describe('dc.pieChart', function () {
441519
expect(d3.select(chart.selectAll('text.pie-slice')[0][2]).text()).toEqual('small');
442520
});
443521
it('remaining slices should be in numerical order', function () {
522+
// Remaining slices are in key order from default ordering function, ascending.
523+
// 55, 66, Others.
524+
525+
// Remember all values are:
526+
// 44 -> 3
527+
// 22 -> 2
528+
// 33 -> 2
529+
// 55 -> 2
530+
// 66 -> 1
531+
532+
// 2, 1, 7
444533
expect(chart.selectAll('text.pie-slice').data().map(dc.pluck('value')))
445-
.toEqual([2,3,5]);
534+
.toEqual([2,1,7]);
446535
});
447536
it('clicking others slice should filter all groups slices', function () {
448537
var event = document.createEvent('MouseEvents');
449538
event.initEvent('click', true, true);
450539
chart.selectAll('.pie-slice path')[0][2].dispatchEvent(event);
451-
expect(chart.filters()).toEqual(['22','55','66','small']);
540+
//22, 33, 44 are what is included in "others" since we are ordering by key
541+
//and capping to only include the two biggest keys.
542+
expect(chart.filters()).toEqual(['22', '33', '44','small']);
452543
chart.selectAll('.pie-slice path')[0][2].dispatchEvent(event);
453544
expect(chart.filters()).toEqual([]);
454545
});
@@ -467,7 +558,7 @@ describe('dc.pieChart', function () {
467558
it('correct values, others row', function () {
468559
chart.cap(1).render();
469560
expect(chart.selectAll('title')[0].map(function (t) {return d3.select(t).text();}))
470-
.toEqual(['F: 220', 'small: 198']);
561+
.toEqual(['T: 198', 'small: 220']);
471562
chart.cap(3); //teardown
472563
});
473564
});

src/cap-mixin.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,38 @@ dc.capMixin = function (_chart) {
5050
return _chart.valueAccessor()(d, i);
5151
};
5252

53+
/**
54+
* Base function orders by crossfilter's group.all.
55+
* This returns all groups, in ascending order, by value.
56+
*
57+
* This function is expected to return items in the same direction,
58+
* ascending but the N biggest where N is cap.
59+
*
60+
* Since computeOrderedGroups is using crossfilter's quicksort, the
61+
* sorted items will always be in ascending order.
62+
*
63+
* This means if we cap, we need to take from N to the end of the array.
64+
*
65+
* Index
66+
* 0-------N-------length
67+
*
68+
* Value
69+
* small -> big
70+
*
71+
*/
5372
_chart.data(function (group) {
5473
if (_cap === Infinity) {
5574
return _chart._computeOrderedGroups(group.all());
5675
} else {
57-
var topRows = group.top(_cap); // ordered by crossfilter group order (default value)
76+
var topRows = group.all(); // ordered by crossfilter group order (default value)
77+
5878
topRows = _chart._computeOrderedGroups(topRows); // re-order using ordering (default key)
79+
80+
if (_cap) {
81+
var start = Math.max(0, topRows.length - _cap);
82+
topRows = topRows.slice(start, topRows.length);
83+
}
84+
5985
if (_othersGrouper) {
6086
return _othersGrouper(topRows);
6187
}

0 commit comments

Comments
 (0)