Skip to content

Commit 9373680

Browse files
committed
Make transforms apply recursively
1 parent 84164e3 commit 9373680

File tree

3 files changed

+89
-21
lines changed

3 files changed

+89
-21
lines changed

src/plots/plots.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,25 +1053,42 @@ plots.supplyTransformDefaults = function(traceIn, traceOut, layout) {
10531053
};
10541054

10551055
function applyTransforms(fullTrace, fullData, layout, fullLayout) {
1056-
var container = fullTrace.transforms,
1057-
dataOut = [fullTrace];
1056+
var dataOut = [];
10581057

1059-
for(var i = 0; i < container.length; i++) {
1060-
var transform = container[i],
1061-
_module = transformsRegistry[transform.type];
1058+
// Takes single trace, returns list of expanded traces
1059+
function recurse(trace, transformIndex) {
1060+
var transformedTraces = [trace];
1061+
var transform = trace.transforms[transformIndex];
1062+
var _module = transformsRegistry[transform.type];
10621063

10631064
if(_module && _module.transform) {
1064-
dataOut = _module.transform(dataOut, {
1065+
transformedTraces = _module.transform([trace], {
10651066
transform: transform,
1066-
fullTrace: fullTrace,
1067+
fullTrace: trace,
10671068
fullData: fullData,
10681069
layout: layout,
10691070
fullLayout: fullLayout,
1070-
transformIndex: i
1071+
transformIndex: transformIndex
10711072
});
10721073
}
1074+
1075+
for(var i = 0; i < transformedTraces.length; i++) {
1076+
if(transformIndex >= transformedTraces[i].transforms.length - 1) {
1077+
// If we get here, we've reached the end of the line and this expanded
1078+
// trace has no more transforms to apply. That is, it's an output trace
1079+
// so we push it onto dataOut.
1080+
dataOut.push(transformedTraces[i]);
1081+
} else {
1082+
// Otherwise there are more transforms to be performed. We don't need
1083+
// to worry about the return value since every transform will eventually
1084+
// reach the end of the line and push its result onto the output.
1085+
recurse(transformedTraces[i], transformIndex + 1);
1086+
}
1087+
}
10731088
}
10741089

1090+
recurse(fullTrace, 0);
1091+
10751092
return dataOut;
10761093
}
10771094

src/transforms/groupby.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,22 @@ function pasteArray(newTrace, trace, j, a) {
137137
);
138138
}
139139

140+
// In order for groups to apply correctly to other transform data (e.g.
141+
// a filter transform), we have to break the connection and clone the
142+
// transforms so that each group writes grouped values into a different
143+
// destination. This function does not break the array reference
144+
// connection between the split transforms it creates. That's handled
145+
// ininialize, which creates a new empty array for each arrayAttr.
146+
function cloneTransforms(newTrace) {
147+
var transforms = newTrace.transforms;
148+
newTrace.transforms = [];
149+
for(var j = 0; j < transforms.length; j++) {
150+
newTrace.transforms[j] = Lib.extendDeepNoArrays({}, transforms[j]);
151+
}
152+
}
153+
140154
function transformOne(trace, state) {
141-
var i;
155+
var i, j;
142156
var opts = state.transform;
143157
var groups = trace.transforms[state.transformIndex].groups;
144158

@@ -163,12 +177,14 @@ function transformOne(trace, state) {
163177

164178
var newTrace = newData[i] = Lib.extendDeepNoArrays({}, trace);
165179

180+
cloneTransforms(newTrace);
181+
166182
arrayAttrs.forEach(initializeArray.bind(null, newTrace));
167183

168-
for(var j = 0; j < len; j++) {
184+
for(j = 0; j < len; j++) {
169185
if(groups[j] !== groupName) continue;
170186

171-
arrayAttrs.forEach(pasteArray.bind(0, newTrace, trace, j));
187+
arrayAttrs.forEach(pasteArray.bind(null, newTrace, trace, j));
172188
}
173189

174190
newTrace.name = groupName;
@@ -180,5 +196,12 @@ function transformOne(trace, state) {
180196
newTrace = Lib.extendDeepNoArrays(newTrace, styleLookup[groupName] || {});
181197
}
182198

199+
for(i = 0; i < newData.length; i++) {
200+
var data = newData[i];
201+
var transforms = data.transforms.slice();
202+
transforms.splice(state.transformIndex, 1);
203+
data.transforms = transforms;
204+
}
205+
183206
return newData;
184207
}

test/jasmine/tests/transform_multi_test.js

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ describe('restyle applied on transforms:', function() {
728728

729729
});
730730

731-
describe('supplyDefaults with groupby + filter', function () {
731+
describe('supplyDefaults with groupby + filter', function() {
732732
function calcDatatoTrace(calcTrace) {
733733
return calcTrace[0].trace;
734734
}
@@ -745,16 +745,16 @@ describe('supplyDefaults with groupby + filter', function () {
745745
return gd.calcdata.map(calcDatatoTrace);
746746
}
747747

748-
it('computes the correct data when filter target blank', function () {
748+
it('computes the correct data when filter target blank', function() {
749749
var out = _transform([{
750750
x: [1, 2, 3, 4, 5, 6, 7],
751751
y: [4, 6, 5, 7, 6, 8, 9],
752752
transforms: [{
753-
type: "filter",
754-
operation: "<",
753+
type: 'filter',
754+
operation: '<',
755755
value: 6.5
756756
}, {
757-
type: "groupby",
757+
type: 'groupby',
758758
groups: [1, 1, 1, 2, 2, 2, 2]
759759
}]
760760
}]);
@@ -766,18 +766,46 @@ describe('supplyDefaults with groupby + filter', function () {
766766
expect(out[1].y).toEqual([7, 6, 8]);
767767
});
768768

769-
it('computes the correct data when filter target present', function () {
769+
it('computes the correct data when fiter + groupby', function() {
770770
var out = _transform([{
771+
x: [5, 4, 3],
772+
y: [6, 5, 4],
773+
}, {
771774
x: [1, 2, 3, 4, 5, 6, 7],
772-
y: [4, 6, 5, 7, 6, 8, 9],
775+
y: [4, 6, 5, 7, 8, 9, 10],
773776
transforms: [{
774-
type: "filter",
777+
type: 'filter',
775778
target: [1, 2, 3, 4, 5, 6, 7],
776-
operation: "<",
779+
operation: '<',
777780
value: 6.5
778781
}, {
779-
type: "groupby",
782+
type: 'groupby',
783+
groups: [1, 1, 1, 2, 2, 2, 2]
784+
}]
785+
}]);
786+
787+
expect(out[0].x).toEqual([5, 4, 3]);
788+
expect(out[0].y).toEqual([6, 5, 4]);
789+
790+
expect(out[1].x).toEqual([1, 2, 3]);
791+
expect(out[1].y).toEqual([4, 6, 5]);
792+
793+
expect(out[2].x).toEqual([4, 5, 6]);
794+
expect(out[2].y).toEqual([7, 8, 9]);
795+
});
796+
797+
it('computes the correct data when groupby + filter', function() {
798+
var out = _transform([{
799+
x: [1, 2, 3, 4, 5, 6, 7],
800+
y: [4, 6, 5, 7, 6, 8, 9],
801+
transforms: [{
802+
type: 'groupby',
780803
groups: [1, 1, 1, 2, 2, 2, 2]
804+
}, {
805+
type: 'filter',
806+
target: [1, 2, 3, 4, 5, 6, 7],
807+
operation: '<',
808+
value: 6.5
781809
}]
782810
}]);
783811

0 commit comments

Comments
 (0)