diff --git a/src/group.js b/src/group.js
deleted file mode 100644
index 1f65900dc9..0000000000
--- a/src/group.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import {InternMap} from "d3-array";
-
-// Like d3.group, but takes an array of values instead of accessor, only
-// supports one level of grouping, and returns an iterable of subsets of the
-// given index rather than a map.
-export function group(index, values) {
- const groups = new InternMap();
- for (const i of index) {
- const value = values[i];
- const group = groups.get(value);
- if (group === undefined) groups.set(value, [i]);
- else group.push(i);
- }
- return groups.values();
-}
diff --git a/src/marks/area.js b/src/marks/area.js
index 7613dde2f2..c9dd26d621 100644
--- a/src/marks/area.js
+++ b/src/marks/area.js
@@ -1,9 +1,9 @@
+import {group, sort} from "d3-array";
import {create} from "d3-selection";
import {area as shapeArea} from "d3-shape";
import {Curve} from "../curve.js";
import {defined} from "../defined.js";
-import {group} from "../group.js";
-import {Mark, indexOf, maybeColor, maybeZero, maybeSort, titleGroup} from "../mark.js";
+import {Mark, indexOf, maybeColor, maybeZero, maybeSort, first, second, titleGroup} from "../mark.js";
import {Style, applyDirectStyles, applyIndirectStyles, applyTransform} from "../style.js";
export class Area extends Mark {
@@ -56,7 +56,7 @@ export class Area extends Mark {
.call(applyIndirectStyles, this)
.call(applyTransform, x, y)
.call(g => g.selectAll()
- .data(Z ? group(I, Z) : [I])
+ .data(Z ? sort(group(I, i => Z[i]), first).map(second) : [I])
.join("path")
.call(applyDirectStyles, this)
.attr("fill", F && (([i]) => color(F[i])))
diff --git a/src/marks/line.js b/src/marks/line.js
index 2c31a32f44..8b83ef5a2b 100644
--- a/src/marks/line.js
+++ b/src/marks/line.js
@@ -1,8 +1,8 @@
+import {group, sort} from "d3-array";
import {create} from "d3-selection";
import {line as shapeLine} from "d3-shape";
import {Curve} from "../curve.js";
import {defined} from "../defined.js";
-import {group} from "../group.js";
import {Mark, indexOf, identity, first, second, maybeColor, maybeSort, titleGroup} from "../mark.js";
import {Style, applyDirectStyles, applyIndirectStyles, applyTransform} from "../style.js";
@@ -53,7 +53,7 @@ export class Line extends Mark {
.call(applyIndirectStyles, this)
.call(applyTransform, x, y, 0.5, 0.5)
.call(g => g.selectAll()
- .data(Z ? group(I, Z) : [I])
+ .data(Z ? sort(group(I, i => Z[i]), first).map(second) : [I])
.join("path")
.call(applyDirectStyles, this)
.attr("fill", F && (([i]) => color(F[i])))
diff --git a/test/output/carsParcoords.svg b/test/output/carsParcoords.svg
index 8504bb90d6..a615396807 100644
--- a/test/output/carsParcoords.svg
+++ b/test/output/carsParcoords.svg
@@ -2890,18 +2890,18 @@
-
-
+
+
-
-
+
-
+
-
+
+
@@ -2910,47 +2910,47 @@
+
-
+
-
+
-
-
+
-
+
+
-
-
+
+
-
@@ -2966,35 +2966,35 @@
-
+
+
-
-
+
-
+
+
-
+
-
-
+
@@ -3013,61 +3013,61 @@
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
@@ -3079,27 +3079,27 @@
-
-
+
-
+
+
-
+
-
+
@@ -3108,36 +3108,36 @@
+
-
+
-
+
-
+
-
-
+
+
-
-
+
-
+
-
+
@@ -3150,20 +3150,20 @@
-
+
-
+
+
-
+
-
@@ -3173,19 +3173,19 @@
-
+
+
-
+
-
-
+
diff --git a/test/output/industryUnemployment.svg b/test/output/industryUnemployment.svg
index 5ad6f191b6..07175b04ea 100644
--- a/test/output/industryUnemployment.svg
+++ b/test/output/industryUnemployment.svg
@@ -69,14 +69,8 @@
-
- Wholesale and Retail Trade
-
-
- Manufacturing
-
-
- Leisure and hospitality
+
+ Agriculture
Business services
@@ -87,29 +81,35 @@
Education and Health
+
+ Finance
+
Government
-
- Finance
+
+ Information
-
- Self-employed
+
+ Leisure and hospitality
+
+
+ Manufacturing
+
+
+ Mining and Extraction
Other
+
+ Self-employed
+
Transportation and Utilities
-
- Information
-
-
- Agriculture
-
-
- Mining and Extraction
+
+ Wholesale and Retail Trade
diff --git a/test/output/industryUnemploymentShare.svg b/test/output/industryUnemploymentShare.svg
index e36b80be38..a62c924f82 100644
--- a/test/output/industryUnemploymentShare.svg
+++ b/test/output/industryUnemploymentShare.svg
@@ -81,14 +81,8 @@
-
- Wholesale and Retail Trade
-
-
- Manufacturing
-
-
- Leisure and hospitality
+
+ Agriculture
Business services
@@ -99,29 +93,35 @@
Education and Health
+
+ Finance
+
Government
-
- Finance
+
+ Information
-
- Self-employed
+
+ Leisure and hospitality
+
+
+ Manufacturing
+
+
+ Mining and Extraction
Other
+
+ Self-employed
+
Transportation and Utilities
-
- Information
-
-
- Agriculture
-
-
- Mining and Extraction
+
+ Wholesale and Retail Trade
diff --git a/test/output/industryUnemploymentStream.svg b/test/output/industryUnemploymentStream.svg
index 8bae1b709b..1144be076e 100644
--- a/test/output/industryUnemploymentStream.svg
+++ b/test/output/industryUnemploymentStream.svg
@@ -35,14 +35,8 @@
-
- Wholesale and Retail Trade
-
-
- Manufacturing
-
-
- Leisure and hospitality
+
+ Agriculture
Business services
@@ -53,29 +47,35 @@
Education and Health
+
+ Finance
+
Government
-
- Finance
+
+ Information
-
- Self-employed
+
+ Leisure and hospitality
+
+
+ Manufacturing
+
+
+ Mining and Extraction
Other
+
+ Self-employed
+
Transportation and Utilities
-
- Information
-
-
- Agriculture
-
-
- Mining and Extraction
+
+ Wholesale and Retail Trade
\ No newline at end of file
diff --git a/test/output/metroUnemploymentHighlight.svg b/test/output/metroUnemploymentHighlight.svg
new file mode 100644
index 0000000000..490febeca3
--- /dev/null
+++ b/test/output/metroUnemploymentHighlight.svg
@@ -0,0 +1,113 @@
+
\ No newline at end of file
diff --git a/test/output/policeDeaths.svg b/test/output/policeDeaths.svg
index 0f7420b1d8..2b9c5d69ab 100644
--- a/test/output/policeDeaths.svg
+++ b/test/output/policeDeaths.svg
@@ -8,10 +8,10 @@
+
-
All other races 6%Black 44%Hispanic 32%White 18%
All other races 15%Black 13%Hispanic 12%White 60%
diff --git a/test/plots/index.js b/test/plots/index.js
index 6b00df1061..e58820d860 100644
--- a/test/plots/index.js
+++ b/test/plots/index.js
@@ -28,6 +28,7 @@ export {default as letterFrequencyLollipop} from "./letter-frequency-lollipop.js
export {default as metroInequalityChange} from "./metro-inequality-change.js";
export {default as metroInequality} from "./metro-inequality.js";
export {default as metroUnemploymentIndex} from "./metro-unemployment-index.js";
+export {default as metroUnemploymentHighlight} from "./metro-unemployment-highlight.js";
export {default as metroUnemploymentMoving} from "./metro-unemployment-moving.js";
export {default as metroUnemploymentRidgeline} from "./metro-unemployment-ridgeline.js";
export {default as metroUnemployment} from "./metro-unemployment.js";
diff --git a/test/plots/metro-unemployment-highlight.js b/test/plots/metro-unemployment-highlight.js
new file mode 100644
index 0000000000..209ee71751
--- /dev/null
+++ b/test/plots/metro-unemployment-highlight.js
@@ -0,0 +1,26 @@
+import * as Plot from "@observablehq/plot";
+import * as d3 from "d3";
+
+export default async function() {
+ const bls = await d3.csv("data/bls-metro-unemployment.csv", d3.autoType);
+ const highlight = d => /, MI /.test(d.division);
+ return Plot.plot({
+ y: {
+ grid: true,
+ label: "↑ Unemployment (%)"
+ },
+ color: {
+ domain: [false, true],
+ range: ["#ccc", "red"]
+ },
+ marks: [
+ Plot.ruleY([0]),
+ Plot.line(bls, {
+ x: "date",
+ y: "unemployment",
+ z: d => `${highlight(d)}/${d.division}`,
+ stroke: highlight
+ })
+ ]
+ });
+}