diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index 903864504e8..2d7c26cc3f2 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -126,12 +126,20 @@ module.exports = function setConvert(ax, fullLayout) { */ function setCategoryIndex(v) { if(v !== null && v !== undefined) { - var c = ax._categories.indexOf(v); - if(c === -1) { + if(ax._categoriesMap === undefined) { + ax._categoriesMap = {}; + } + + if(ax._categoriesMap[v] !== undefined) { + return ax._categoriesMap[v]; + } else { ax._categories.push(v); - return ax._categories.length - 1; + + var curLength = ax._categories.length - 1; + ax._categoriesMap[v] = curLength; + + return curLength; } - return c; } return BADNUM; } @@ -139,9 +147,12 @@ module.exports = function setConvert(ax, fullLayout) { function getCategoryIndex(v) { // d2l/d2c variant that that won't add categories but will also // allow numbers to be mapped to the linearized axis positions - var index = ax._categories.indexOf(v); - if(index !== -1) return index; - if(typeof v === 'number') return v; + if(ax._categoriesMap) { + var index = ax._categoriesMap[v]; + if(index !== undefined) return index; + } + + if(typeof v === 'number') { return v; } } function l2p(v) { @@ -325,6 +336,8 @@ module.exports = function setConvert(ax, fullLayout) { // TODO cleaner way to handle this case if(!ax._categories) ax._categories = []; + // Add a map to optimize the performance of category collection + if(!ax._categoriesMap) ax._categoriesMap = {}; // make sure we have a domain (pull it in from the axis // this one is overlaying if necessary) diff --git a/src/plots/plots.js b/src/plots/plots.js index 6f4dfcf913e..d84500bc07b 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1950,6 +1950,13 @@ plots.doCalcdata = function(gd, traces) { // to be filled in later by ax.d2c for(i = 0; i < axList.length; i++) { axList[i]._categories = axList[i]._initialCategories.slice(); + + // Build the lookup map for initialized categories + axList[i]._categoriesMap = {}; + for(j = 0; j < axList[i]._categories.length; j++) { + axList[i]._categoriesMap[axList[i]._categories[j]] = j; + } + if(axList[i].type === 'category') hasCategoryAxis = true; } @@ -1994,6 +2001,8 @@ plots.doCalcdata = function(gd, traces) { axList[i]._min = []; axList[i]._max = []; axList[i]._categories = []; + // Reset the look up map + axList[i]._categoriesMap = {}; } } diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 7da03ffe2fe..8e367807a70 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -51,13 +51,13 @@ module.exports = function plot(gd, plotinfo, cdscatter, transitionOpts, makeOnCo // Sort the traces, once created, so that the ordering is preserved even when traces // are shown and hidden. This is needed since we're not just wiping everything out // and recreating on every update. - for(i = 0, uids = []; i < cdscatter.length; i++) { - uids[i] = cdscatter[i][0].trace.uid; + for(i = 0, uids = {}; i < cdscatter.length; i++) { + uids[cdscatter[i][0].trace.uid] = i; } scatterlayer.selectAll('g.trace').sort(function(a, b) { - var idx1 = uids.indexOf(a[0].trace.uid); - var idx2 = uids.indexOf(b[0].trace.uid); + var idx1 = uids[a[0].trace.uid]; + var idx2 = uids[b[0].trace.uid]; return idx1 > idx2 ? 1 : -1; }); diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 6f82ef2b067..73c04444b2e 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -1685,6 +1685,7 @@ describe('Test axes', function() { var ax = { type: 'category', _categories: ['a', 'b', 'c', 'd'], + _categoriesMap: {'a': 0, 'b': 1, 'c': 2, 'd': 3}, tickmode: 'array', tickvals: ['a', 1, 1.5, 'c', 2.7, 3, 'e', 4, 5, -2], ticktext: ['A!', 'B?', 'B->C'], @@ -1804,6 +1805,7 @@ describe('Test axes', function() { function _autoBin(x, ax, nbins) { ax._categories = []; + ax._categoriesMap = {}; Axes.setConvert(ax); var d = ax.makeCalcdata({ x: x }, 'x');