Skip to content

Commit b5366f9

Browse files
mbostockFil
andauthored
preserve group input order (#1959)
* preserve group input order * sort: "z"; fix tests * reduce z docs * fix tests * Update docs/transforms/group.md Co-authored-by: Philippe Rivière <[email protected]> --------- Co-authored-by: Philippe Rivière <[email protected]>
1 parent 3a9581b commit b5366f9

File tree

70 files changed

+2313
-2299
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+2313
-2299
lines changed

docs/transforms/bin.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ The following named reducers are supported:
274274
* *y* - the middle of the bin’s *y* extent (when binning on *y*)
275275
* *y1* - the lower bound of the bin’s *y* extent (when binning on *y*)
276276
* *y2* - the upper bound of the bin’s *y* extent (when binning on *y*)
277+
* *z* <VersionBadge pr="1959" /> - the bin’s *z* value (*z*, *fill*, or *stroke*)
277278

278279
In addition, a reducer may be specified as:
279280

docs/transforms/group.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ Although barX applies an implicit stackX transform, [textX](../marks/text.md) do
329329

330330
## Group options
331331

332-
Given input *data* = [*d₀*, *d₁*, *d₂*, …], by default the resulting grouped data is an array of arrays where each inner array is a subset of the input data such as [[*d₁*, *d₂*, …], [*d₀*, …], …]. Each inner array is in input order. The outer array is in natural ascending order according to the associated dimension (*x* then *y*).
332+
Given input *data* = [*d₀*, *d₁*, *d₂*, …], by default the resulting grouped data is an array of arrays where each inner array is a subset of the input data such as [[*d₁*, *d₂*, …], [*d₀*, …], …]. Each inner array is in input order. The outer array is in input order according to the first element of each group.
333333

334334
By specifying a different reducer for the **data** output, as described below, you can change how the grouped data is computed. The outputs may also include **filter** and **sort** options specified as reducers, and a **reverse** option to reverse the order of generated groups. By default, empty groups are omitted, and non-empty groups are generated in ascending (natural) order.
335335

@@ -370,6 +370,7 @@ The following named reducers are supported:
370370
* *identity* - the array of values
371371
* *x* <VersionBadge version="0.6.12" pr="1916" /> - the group’s *x* value (when grouping on *x*)
372372
* *y* <VersionBadge version="0.6.12" pr="1916" /> - the group’s *y* value (when grouping on *y*)
373+
* *z* <VersionBadge pr="1959" /> - the group’s *z* value (*z*, *fill*, or *stroke*)
373374

374375
In addition, a reducer may be specified as:
375376

src/transforms/bin.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export interface BinOptions {
117117
* - *y* - the middle of the bin’s **y** extent (when binning on **y**)
118118
* - *y1* - the lower bound of the bin’s **y** extent (when binning on **y**)
119119
* - *y2* - the upper bound of the bin’s **y** extent (when binning on **y**)
120+
* - *z* - the bin’s **z** value (when grouping on **z**, **fill**, or **stroke**)
120121
* - a function that takes an array of values and returns the reduced value
121122
* - an object that implements the *reduceIndex* method
122123
*
@@ -132,7 +133,8 @@ export type BinReducer =
132133
| "x2"
133134
| "y"
134135
| "y1"
135-
| "y2";
136+
| "y2"
137+
| "z";
136138

137139
/**
138140
* A shorthand functional bin reducer implementation: given an array of input

src/transforms/bin.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ import {
4141
maybeSubgroup,
4242
reduceCount,
4343
reduceFirst,
44-
reduceIdentity
44+
reduceIdentity,
45+
reduceZ
4546
} from "./group.js";
4647
import {maybeInsetX, maybeInsetY} from "./inset.js";
4748

@@ -180,6 +181,7 @@ function binn(
180181
for (const [f, I] of maybeGroup(facet, G)) {
181182
for (const [k, g] of maybeGroup(I, K)) {
182183
for (const [b, extent] of bin(g)) {
184+
if (G) extent.z = f;
183185
if (filter && !filter.reduce(b, extent)) continue;
184186
groupFacet.push(i++);
185187
groupData.push(reduceData.reduceIndex(b, data, extent));
@@ -190,7 +192,7 @@ function binn(
190192
if (BX1) BX1.push(extent.x1), BX2.push(extent.x2);
191193
if (BY1) BY1.push(extent.y1), BY2.push(extent.y2);
192194
for (const o of outputs) o.reduce(b, extent);
193-
if (sort) sort.reduce(b);
195+
if (sort) sort.reduce(b, extent);
194196
}
195197
}
196198
}
@@ -355,6 +357,8 @@ function maybeBinReduceFallback(reduce) {
355357
return reduceY1;
356358
case "y2":
357359
return reduceY2;
360+
case "z":
361+
return reduceZ;
358362
}
359363
throw new Error(`invalid bin reduce: ${reduce}`);
360364
}

src/transforms/group.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ export interface GroupOutputOptions<T = Reducer> {
4444
* - a generic reducer name, such as *count* or *first*
4545
* - *x* - the group’s **x** value (when grouping on **x**)
4646
* - *y* - the group’s **y** value (when grouping on **y**)
47+
* - *z* - the group’s **z** value (when grouping on **z**, **fill**, or **stroke**)
4748
* - a function that takes an array of values and returns the reduced value
4849
* - an object that implements the *reduceIndex* method
4950
*
5051
* When a reducer function or implementation is used with the group transform,
5152
* it is passed the group extent {x, y} as an additional argument.
5253
*/
53-
export type GroupReducer = Reducer | GroupReducerFunction | GroupReducerImplementation | "x" | "y";
54+
export type GroupReducer = Reducer | GroupReducerFunction | GroupReducerImplementation | "x" | "y" | "z";
5455

5556
/**
5657
* A shorthand functional group reducer implementation: given an array of input

src/transforms/group.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
import {ascendingDefined} from "../defined.js";
1818
import {
1919
column,
20-
first,
2120
identity,
2221
isObject,
2322
isTemporal,
@@ -137,6 +136,7 @@ function groupn(
137136
const extent = {data};
138137
if (X) extent.x = x;
139138
if (Y) extent.y = y;
139+
if (G) extent.z = f;
140140
if (filter && !filter.reduce(g, extent)) continue;
141141
groupFacet.push(i++);
142142
groupData.push(reduceData.reduceIndex(g, data, extent));
@@ -230,12 +230,7 @@ export function maybeEvaluator(name, reduce, inputs, asReduce = maybeReduce) {
230230
}
231231

232232
export function maybeGroup(I, X) {
233-
return X
234-
? sort(
235-
grouper(I, (i) => X[i]),
236-
first
237-
)
238-
: [[, I]];
233+
return X ? grouper(I, (i) => X[i]) : [[, I]];
239234
}
240235

241236
export function maybeReduce(reduce, value, fallback = invalidReduce) {
@@ -309,6 +304,8 @@ function maybeGroupReduceFallback(reduce) {
309304
return reduceX;
310305
case "y":
311306
return reduceY;
307+
case "z":
308+
return reduceZ;
312309
}
313310
throw new Error(`invalid group reduce: ${reduce}`);
314311
}
@@ -437,6 +434,12 @@ const reduceY = {
437434
}
438435
};
439436

437+
export const reduceZ = {
438+
reduceIndex(I, X, {z}) {
439+
return z;
440+
}
441+
};
442+
440443
export function find(test) {
441444
if (typeof test !== "function") throw new Error(`invalid test function: ${test}`);
442445
return {

test/output/athletesBirthdays.svg

Lines changed: 16 additions & 16 deletions
Loading

0 commit comments

Comments
 (0)