Skip to content

Commit 0be666f

Browse files
committed
first cut sort transform
1 parent ef0946c commit 0be666f

File tree

4 files changed

+394
-1
lines changed

4 files changed

+394
-1
lines changed

lib/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ Plotly.register([
5656
//
5757
Plotly.register([
5858
require('./filter'),
59-
require('./groupby')
59+
require('./groupby'),
60+
require('./sort')
6061
]);
6162

6263
// components

lib/sort.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2017, 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+
'use strict';
10+
11+
module.exports = require('../src/transforms/sort');

src/transforms/sort.js

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/**
2+
* Copyright 2012-2017, 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+
'use strict';
10+
11+
var Lib = require('../lib');
12+
var PlotSchema = require('../plot_api/plot_schema');
13+
var axisIds = require('../plots/cartesian/axis_ids');
14+
var autoType = require('../plots/cartesian/axis_autotype');
15+
var setConvert = require('../plots/cartesian/set_convert');
16+
17+
exports.moduleType = 'transform';
18+
19+
exports.name = 'sort';
20+
21+
exports.attributes = {
22+
enabled: {
23+
valType: 'boolean',
24+
dflt: true,
25+
description: [
26+
'Determines whether this sort transform is enabled or disabled.'
27+
].join(' ')
28+
},
29+
target: {
30+
valType: 'string',
31+
strict: true,
32+
noBlank: true,
33+
arrayOk: true,
34+
dflt: 'x',
35+
description: [
36+
'Sets the target by which the sort transform is applied.',
37+
38+
'If a string, *target* is assumed to be a reference to a data array',
39+
'in the parent trace object.',
40+
'To sort about nested variables, use *.* to access them.',
41+
'For example, set `target` to *marker.size* to sort',
42+
'about the marker size array.',
43+
44+
'If an array, *target* is then the data array by which',
45+
'the sort transform is applied.'
46+
].join(' ')
47+
},
48+
order: {
49+
valType: 'enumerated',
50+
values: ['ascending', 'descending'],
51+
dflt: 'ascending',
52+
description: [
53+
'Sets the sort transform order.'
54+
].join(' ')
55+
}
56+
};
57+
58+
exports.supplyDefaults = function(transformIn) {
59+
var transformOut = {};
60+
61+
function coerce(attr, dflt) {
62+
return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt);
63+
}
64+
65+
var enabled = coerce('enabled');
66+
67+
if(enabled) {
68+
coerce('target');
69+
coerce('order');
70+
}
71+
72+
return transformOut;
73+
};
74+
75+
exports.calcTransform = function(gd, trace, opts) {
76+
if(!opts.enabled) return;
77+
78+
var target = opts.target;
79+
var targetArray = getTargetArray(trace, target);
80+
var len = targetArray.length;
81+
82+
if(!len) return;
83+
84+
var arrayAttrs = PlotSchema.findArrayAttributes(trace);
85+
var d2c = getDataToCoordFunc(gd, trace, target, targetArray);
86+
var indices = getIndices(opts, targetArray, d2c);
87+
88+
for(var i = 0; i < arrayAttrs.length; i++) {
89+
var np = Lib.nestedProperty(trace, arrayAttrs[i]);
90+
var arrayCopy = np.get().slice();
91+
var arrayNew = new Array(len);
92+
93+
for(var j = 0; j < len; j++) {
94+
arrayNew[j] = arrayCopy[indices[j]];
95+
}
96+
97+
np.set(arrayNew);
98+
}
99+
};
100+
101+
// TODO reuse for filter.js
102+
function getTargetArray(trace, target) {
103+
if(typeof target === 'string' && target) {
104+
var array = Lib.nestedProperty(trace, target).get();
105+
106+
return Array.isArray(array) ? array : [];
107+
}
108+
else if(Array.isArray(target)) return target.slice();
109+
110+
return false;
111+
}
112+
113+
// TODO reuse for filter.js
114+
function getDataToCoordFunc(gd, trace, target, targetArray) {
115+
var ax;
116+
117+
// If target points to an axis, use the type we already have for that
118+
// axis to find the data type. Otherwise use the values to autotype.
119+
if(target === 'x' || target === 'y' || target === 'z') {
120+
ax = axisIds.getFromTrace(gd, trace, target);
121+
}
122+
// In the case of an array target, make a mock data array
123+
// and call supplyDefaults to the data type and
124+
// setup the data-to-calc method.
125+
else if(Array.isArray(target)) {
126+
ax = {
127+
type: autoType(targetArray),
128+
// TODO does this still work with the new hash object
129+
_categories: []
130+
};
131+
setConvert(ax);
132+
133+
// build up ax._categories (usually done during ax.makeCalcdata()
134+
if(ax.type === 'category') {
135+
for(var i = 0; i < targetArray.length; i++) {
136+
ax.d2c(targetArray[i]);
137+
}
138+
}
139+
}
140+
141+
// if 'target' has corresponding axis
142+
// -> use setConvert method
143+
if(ax) return ax.d2c;
144+
145+
// special case for 'ids'
146+
// -> cast to String
147+
if(target === 'ids') return function(v) { return String(v); };
148+
149+
// otherwise (e.g. numeric-array of 'marker.color' or 'marker.size')
150+
// -> cast to Number
151+
return function(v) { return +v; };
152+
}
153+
154+
function getIndices(opts, targetArray, d2c) {
155+
var len = targetArray.length;
156+
var indices = new Array(len);
157+
158+
var sortedArray = targetArray
159+
.slice()
160+
.sort(getSortFunc(opts, d2c));
161+
162+
for(var i = 0; i < len; i++) {
163+
var vTarget = targetArray[i];
164+
165+
for(var j = 0; j < len; j++) {
166+
var vSorted = sortedArray[j];
167+
168+
if(vTarget === vSorted) {
169+
indices[j] = i;
170+
171+
// clear sortedArray item to get correct
172+
// index of duplicate items (if any)
173+
sortedArray[j] = null;
174+
break;
175+
}
176+
}
177+
}
178+
179+
return indices;
180+
}
181+
182+
function getSortFunc(opts, d2c) {
183+
switch(opts.order) {
184+
case 'ascending':
185+
return function(a, b) { return d2c(a) - d2c(b); };
186+
case 'descending':
187+
return function(a, b) { return d2c(b) - d2c(a); };
188+
}
189+
}

0 commit comments

Comments
 (0)