-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Sort transform #1609
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sort transform #1609
Changes from 7 commits
0be666f
36f7acd
1a21ad9
823375c
2dd1333
9c70ee9
4f91957
4e8efe4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* Copyright 2012-2017, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
module.exports = require('../src/transforms/sort'); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/** | ||
* Copyright 2012-2017, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var Lib = require('../lib'); | ||
var PlotSchema = require('../plot_api/plot_schema'); | ||
var Axes = require('../plots/cartesian/axes'); | ||
|
||
exports.moduleType = 'transform'; | ||
|
||
exports.name = 'sort'; | ||
|
||
exports.attributes = { | ||
enabled: { | ||
valType: 'boolean', | ||
dflt: true, | ||
description: [ | ||
'Determines whether this sort transform is enabled or disabled.' | ||
].join(' ') | ||
}, | ||
target: { | ||
valType: 'string', | ||
strict: true, | ||
noBlank: true, | ||
arrayOk: true, | ||
dflt: 'x', | ||
description: [ | ||
'Sets the target by which the sort transform is applied.', | ||
|
||
'If a string, *target* is assumed to be a reference to a data array', | ||
'in the parent trace object.', | ||
'To sort about nested variables, use *.* to access them.', | ||
'For example, set `target` to *marker.size* to sort', | ||
'about the marker size array.', | ||
|
||
'If an array, *target* is then the data array by which', | ||
'the sort transform is applied.' | ||
].join(' ') | ||
}, | ||
order: { | ||
valType: 'enumerated', | ||
values: ['ascending', 'descending'], | ||
dflt: 'ascending', | ||
description: [ | ||
'Sets the sort transform order.' | ||
].join(' ') | ||
} | ||
}; | ||
|
||
exports.supplyDefaults = function(transformIn) { | ||
var transformOut = {}; | ||
|
||
function coerce(attr, dflt) { | ||
return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt); | ||
} | ||
|
||
var enabled = coerce('enabled'); | ||
|
||
if(enabled) { | ||
coerce('target'); | ||
coerce('order'); | ||
} | ||
|
||
return transformOut; | ||
}; | ||
|
||
exports.calcTransform = function(gd, trace, opts) { | ||
if(!opts.enabled) return; | ||
|
||
var targetArray = Lib.getTargetArray(trace, opts); | ||
if(!targetArray) return; | ||
|
||
var target = opts.target; | ||
var len = targetArray.length; | ||
var arrayAttrs = PlotSchema.findArrayAttributes(trace); | ||
var d2c = Axes.getDataToCoordFunc(gd, trace, target, targetArray); | ||
var indices = getIndices(opts, targetArray, d2c); | ||
|
||
for(var i = 0; i < arrayAttrs.length; i++) { | ||
var np = Lib.nestedProperty(trace, arrayAttrs[i]); | ||
var arrayOld = np.get(); | ||
var arrayNew = new Array(len); | ||
|
||
for(var j = 0; j < len; j++) { | ||
arrayNew[j] = arrayOld[indices[j]]; | ||
} | ||
|
||
np.set(arrayNew); | ||
} | ||
}; | ||
|
||
function getIndices(opts, targetArray, d2c) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Challenge to all plotly.js contributors: find a faster sort algorithm that this O(n^2) attempt. Note that binary search is nice, but doesn't work so great for arrays with duplicate items. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I quite understand. For the overall sort? Can you use something like stable? 1. assemble There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stable sorting may be useful, e.g. the user presorted based on some criteria, we'd keep that, even if this new facility will support sorting based on multiple fields. Stable sorting is simple with the built-in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @monfera , that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ^^ put in a bit more code for copy/paste into console in isolation and fixed a typo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ^^ another fix and using actual objects to verify stability of the sorting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @monfera how does ^^ handle duplicate items? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some interesting results in https://gist.github.com/etpinard/86949a01309d5505fab8ccca2b0cd875 Looks like my dumb attempt isn't that bad after all (barring any typos ...) |
||
var len = targetArray.length; | ||
var indices = new Array(len); | ||
|
||
var sortedArray = targetArray | ||
.slice() | ||
.sort(getSortFunc(opts, d2c)); | ||
|
||
for(var i = 0; i < len; i++) { | ||
var vTarget = targetArray[i]; | ||
|
||
for(var j = 0; j < len; j++) { | ||
var vSorted = sortedArray[j]; | ||
|
||
if(vTarget === vSorted) { | ||
indices[j] = i; | ||
|
||
// clear sortedArray item to get correct | ||
// index of duplicate items (if any) | ||
sortedArray[j] = null; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
return indices; | ||
} | ||
|
||
function getSortFunc(opts, d2c) { | ||
switch(opts.order) { | ||
case 'ascending': | ||
return function(a, b) { return d2c(a) - d2c(b); }; | ||
case 'descending': | ||
return function(a, b) { return d2c(b) - d2c(a); }; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose ascending and descending to be familiar with axis
categoryorder
.