Skip to content

Commit e36d163

Browse files
committed
plotly#189 category ordering - final, bisecting version
1 parent 94e0133 commit e36d163

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
10+
'use strict';
11+
12+
var d3 = require('d3');
13+
14+
// flattenUniqueSort :: String -> Function -> [[String]] -> [String]
15+
function flattenUniqueSort(axisLetter, sortFunction, data) {
16+
17+
// Bisection based insertion sort of distinct values for logarithmic time complexity.
18+
// Can't use a hashmap, which is O(1), because ES5 maps coerce keys to strings. If it ever becomes a bottleneck,
19+
// code can be separated: a hashmap (JS object) based version if all values encountered are strings; and
20+
// downgrading to this O(log(n)) array on the first encounter of a non-string value.
21+
22+
var categoryArray = [];
23+
24+
var traceLines = data.map(function(d) {return d[axisLetter];});
25+
26+
var i, j, tracePoints, category, insertionIndex;
27+
28+
var bisector = d3.bisector(sortFunction).left;
29+
30+
for(i = 0; i < traceLines.length; i++) {
31+
32+
tracePoints = traceLines[i];
33+
34+
for(j = 0; j < tracePoints.length; j++) {
35+
36+
category = tracePoints[j];
37+
38+
// skip loop: ignore null and undefined categories
39+
if(category === null || category === undefined) continue;
40+
41+
insertionIndex = bisector(categoryArray, category);
42+
43+
// skip loop on already encountered values
44+
if(insertionIndex < categoryArray.length - 1 && categoryArray[insertionIndex] === category) continue;
45+
46+
// insert value
47+
categoryArray.splice(insertionIndex, 0, category);
48+
}
49+
}
50+
51+
return categoryArray;
52+
}
53+
54+
55+
/**
56+
* This pure function returns the ordered categories for specified axisLetter, categorymode, categorylist and data.
57+
*
58+
* If categorymode is 'array', the result is a fresh copy of categorylist, or if unspecified, an empty array.
59+
*
60+
* If categorymode is 'category ascending' or 'category descending', the result is an array of ascending or descending
61+
* order of the unique categories encountered in the data for specified axisLetter.
62+
*
63+
* See cartesian/layout_attributes.js for the definition of categorymode and categorylist
64+
*
65+
*/
66+
67+
// orderedCategories :: String -> String -> [String] -> [[String]] -> [String]
68+
module.exports = function orderedCategories(axisLetter, categorymode, categorylist, data) {
69+
70+
switch(categorymode) {
71+
case 'array': return Array.isArray(categorylist) ? categorylist.slice() : [];
72+
case 'category ascending': return flattenUniqueSort(axisLetter, d3.ascending, data);
73+
case 'category descending': return flattenUniqueSort(axisLetter, d3.descending, data);
74+
case 'trace': return [];
75+
default: return [];
76+
}
77+
};

0 commit comments

Comments
 (0)