Skip to content

Commit

Permalink
d3.index, d3.indexes
Browse files Browse the repository at this point in the history
closes #136
  • Loading branch information
Fil authored and mbostock committed Aug 23, 2020
1 parent 3d3c9a7 commit 1f92b6c
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 1 deletion.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,39 @@ In the near future, [*selection*.data](https://github.com/d3/d3-selection/blob/m

Equivalent to [group](#group), but returns nested arrays instead of nested maps.

<a name="index" href="#index">#</a> d3.<b>index</b>(<i>iterable</i>, <i>...keys</i>) · [Source](https://github.com/d3/d3-array/blob/master/src/group.js)<!-- , [Examples](https://observablehq.com/@d3/d3-index) -->

Equivalent to [group](#group) but returns a unique value per compound key instead of an array, throwing if the key is not unique.

For example, given the data defined above,

```js
d3.index(data, d => d.amount)
```

returns

```js
Map(4) {
"34.0" => Object {name: "jim", amount: "34.0", date: "11/12/2015"}
"120.11" => Object {name: "carl", amount: "120.11", date: "11/12/2015"}
"12.01" => Object {name: "stacy", amount: "12.01", date: "01/04/2016"}
"34.05" => Object {name: "stacy", amount: "34.05", date: "01/04/2016"}
}
```

On the other hand,

```js
d3.index(data, d => d.name)
```

throws an error because two objects share the same name.

<a name="indexes" href="#indexes">#</a> d3.<b>indexes</b>(<i>iterable</i>, <i>...keys</i>) · [Source](https://github.com/d3/d3-array/blob/master/src/group.js)<!-- , [Examples](https://observablehq.com/@d3/d3-index) -->

Equivalent to [index](#index), but returns nested arrays instead of nested maps.

<a name="rollup" href="#rollup">#</a> d3.<b>rollup</b>(<i>iterable</i>, <i>reduce</i>, <i>...keys</i>) · [Source](https://github.com/d3/d3-array/blob/master/src/group.js), [Examples](https://observablehq.com/@d3/d3-group-d3-rollup)

[Groups](#group) and reduces the specified *iterable* of values into a Map from *key* to value. For example, given some data:
Expand Down
13 changes: 13 additions & 0 deletions src/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ export function rollups(values, reduce, ...keys) {
return nest(values, Array.from, reduce, keys);
}

export function index(values, ...keys) {
return nest(values, identity, unique, keys);
}

export function indexes(values, ...keys) {
return nest(values, Array.from, unique, keys);
}

function unique(values) {
if (values.length !== 1) throw new Error("duplicate key");
return values[0];
}

function nest(values, map, reduce, keys) {
return (function regroup(values, i) {
if (i >= keys.length) return reduce(values);
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {default as fsum, Adder} from "./fsum.js";
export {default as group, groups, rollup, rollups} from "./group.js";
export {default as group, groups, index, indexes, rollup, rollups} from "./group.js";
export {default as bin, default as histogram} from "./bin.js"; // Deprecated; use bin.
export {default as thresholdFreedmanDiaconis} from "./threshold/freedmanDiaconis.js";
export {default as thresholdScott} from "./threshold/scott.js";
Expand Down
67 changes: 67 additions & 0 deletions test/index-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const tape = require("tape-await");
const d3 = require("../");

const data = [
{name: "jim", amount: 34.0, date: "11/12/2015"},
{name: "carl", amount: 120.11, date: "11/12/2015"},
{name: "stacy", amount: 12.01, date: "01/04/2016"},
{name: "stacy", amount: 34.05, date: "01/04/2016"}
];

tape("indexes(data, ...keys) returns the expected nested arrays", (test) => {
test.deepEqual(
d3.indexes(data, d => d.amount),
[
[34.0, {name: "jim", amount: 34.0, date: "11/12/2015"}],
[120.11, {name: "carl", amount: 120.11, date: "11/12/2015"}],
[12.01, {name: "stacy", amount: 12.01, date: "01/04/2016"}],
[34.05, {name: "stacy", amount: 34.05, date: "01/04/2016"}]
]
);
test.deepEqual(
d3.indexes(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"}],
[34.05, {name: "stacy", amount: 34.05, date: "01/04/2016"}]
]
]
]
);
});

tape("index(data, ...keys) returns the expected map", (test) => {
test.deepEqual(
entries(d3.index(data, d => d.amount), 1),
d3.indexes(data, d => d.amount)
);
test.deepEqual(
entries(d3.index(data, d => d.name, d => d.amount), 2),
d3.indexes(data, d => d.name, d => d.amount)
);
});

tape("index(data, ...keys) throws on a non-unique key", (test) => {
test.throws(() => d3.index(data, d => d.name));
test.throws(() => d3.index(data, d => d.date));
});

function entries(map, depth) {
return depth > 0
? Array.from(map, ([k, v]) => [k, entries(v, depth - 1)])
: map;
}

0 comments on commit 1f92b6c

Please sign in to comment.