diff --git a/README.md b/README.md
index 3a1b0dad..bc21038e 100644
--- a/README.md
+++ b/README.md
@@ -417,6 +417,10 @@ This produces:
Equivalent to [group](#group), but returns nested arrays instead of nested maps.
+# d3.flatGroup(iterable, ...keys) · [Source](https://github.com/d3/d3-array/blob/master/src/group.js), [Examples](https://observablehq.com/@d3/d3-flatgroup)
+
+Equivalent to [group](#group), but returns the result as a flat array of [key0, key1, …, values] instead of nested maps.
+
# d3.index(iterable, ...keys) · [Source](https://github.com/d3/d3-array/blob/master/src/group.js), [Examples](https://observablehq.com/@d3/d3-group)
Equivalent to [group](#group) but returns a unique value per compound key instead of an array, throwing if the key is not unique.
@@ -507,6 +511,10 @@ To convert a Map to an Array, use [Array.from](https://developer.mozilla.org/en-
Equivalent to [rollup](#rollup), but returns nested arrays instead of nested maps.
+# d3.flatRollup(iterable, ...keys) · [Source](https://github.com/d3/d3-array/blob/master/src/group.js), [Examples](https://observablehq.com/@d3/d3-flatgroup)
+
+Equivalent to [rollup](#rollup), but returns the result as a flat array of [key0, key1, …, reduced value] instead of nested maps.
+
# d3.groupSort(iterable, comparator, key) · [Source](https://github.com/d3/d3-array/blob/master/src/groupSort.js), [Examples](https://observablehq.com/@d3/d3-groupsort)
# d3.groupSort(iterable, accessor, key)
diff --git a/src/group.js b/src/group.js
index 4814571f..0c30d7e9 100644
--- a/src/group.js
+++ b/src/group.js
@@ -9,14 +9,21 @@ export function groups(values, ...keys) {
return nest(values, Array.from, identity, keys);
}
-export function flatGroup(values, ...keys) {
- let groups = groups(values, ...keys);
+function flatten(groups, keys) {
for (let i = 1, n = keys.length; i < n; ++i) {
groups = groups.flatMap(g => g.pop().map(([key, value]) => [...g, key, value]));
}
return groups;
}
+export function flatGroup(values, ...keys) {
+ return flatten(groups(values, ...keys), keys);
+}
+
+export function flatRollup(values, reduce, ...keys) {
+ return flatten(rollups(values, reduce, ...keys), keys);
+}
+
export function rollup(values, reduce, ...keys) {
return nest(values, identity, reduce, keys);
}
diff --git a/src/index.js b/src/index.js
index 44c5ac66..209d7b2f 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,7 +8,7 @@ export {default as descending} from "./descending.js";
export {default as deviation} from "./deviation.js";
export {default as extent} from "./extent.js";
export {Adder, fsum, fcumsum} from "./fsum.js";
-export {default as group, groups, index, indexes, rollup, rollups} from "./group.js";
+export {default as group, flatGroup, flatRollup, groups, index, indexes, rollup, rollups} from "./group.js";
export {default as groupSort} from "./groupSort.js";
export {default as bin, default as histogram} from "./bin.js"; // Deprecated; use bin.
export {default as thresholdFreedmanDiaconis} from "./threshold/freedmanDiaconis.js";
diff --git a/test/group-test.js b/test/group-test.js
index 24eaa7b3..1909f069 100644
--- a/test/group-test.js
+++ b/test/group-test.js
@@ -1,5 +1,5 @@
import assert from "assert";
-import {group} from "../src/index.js";
+import {group, flatGroup, flatRollup} from "../src/index.js";
const data = [
{name: "jim", amount: "34.0", date: "11/12/2015"},
@@ -129,6 +129,41 @@ it("group(data, accessor) interns keys", () => {
assert.strictEqual([...map.keys()][1], b);
});
+it("flatGroup(data, accessor, accessor) returns the expected array", () => {
+ assert.deepStrictEqual(
+ flatGroup(data, d => d.name, d => d.amount),
+ [
+ ['jim', '34.0', [{name: 'jim', amount: '34.0', date: '11/12/2015'}]],
+ ['carl', '120.11', [{name: 'carl', amount: '120.11', date: '11/12/2015'}]],
+ ['stacy', '12.01', [{name: 'stacy', amount: '12.01', date: '01/04/2016'}]],
+ ['stacy', '34.05', [{name: 'stacy', amount: '34.05', date: '01/04/2016'}]]
+ ]
+ );
+});
+
+it("flatRollup(data, reduce, accessor) returns the expected array", () => {
+ assert.deepStrictEqual(
+ flatRollup(data, v => v.length, d => d.name),
+ [
+ ['jim', 1],
+ ['carl', 1],
+ ['stacy', 2]
+ ]
+ );
+});
+
+it("flatRollup(data, reduce, accessor, accessor) returns the expected array", () => {
+ assert.deepStrictEqual(
+ flatRollup(data, v => v.length, d => d.name, d => d.amount),
+ [
+ ['jim', '34.0', 1],
+ ['carl', '120.11', 1],
+ ['stacy', '12.01', 1],
+ ['stacy', '34.05', 1]
+ ]
+ );
+});
+
function entries(map, depth) {
if (depth > 0) {
return Array.from(map, ([k, v]) => [k, entries(v, depth - 1)]);