Skip to content

Commit

Permalink
Add ordinal invert.
Browse files Browse the repository at this point in the history
  • Loading branch information
jheer committed Jun 7, 2016
1 parent d50b051 commit 67c7a58
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 5 deletions.
13 changes: 13 additions & 0 deletions README.md
Expand Up @@ -734,6 +734,19 @@ Constructs a new ordinal scale with an empty [domain](#ordinal_domain) and the s

Given a *value* in the input [domain](#ordinal_domain), returns the corresponding value in the output [range](#ordinal_range). If the given *value* is not in the scale’s [domain](#ordinal_domain), returns the [unknown](#ordinal_value); or, if the unknown value is [implicit](#implicit) (the default), then the *value* is implicitly added to the domain and the next-available value in the range is assigned to *value*, such that this and subsequent invocations of the scale given the same input *value* return the same output value.

<a name="ordinal_invert" href="#ordinal_invert">#</a> <i>ordinal</i>.<b>invert</b>(<i>value</i>)

Given a *value* from the [range](#ordinal_range), returns the first corresponding value from the [domain](#ordinal_domain). If the range includes duplicate values, this method returns the corresponding value with the lowest index in the domain array. For example:

```js
var ordinal = d3.scaleOrdinal()
.domain(["a", "b", "c"])
.range(["red", "white", "red"]);

ordinal.invert("red"); // "a"
ordinal.invert("white"); // "b"
```

<a name="ordinal_domain" href="#ordinal_domain">#</a> <i>ordinal</i>.<b>domain</b>([<i>domain</i>])

If *domain* is specified, sets the domain to the specified array of values. The first element in *domain* will be mapped to the first element in the range, the second domain value to the second range value, and so on. Domain values are stored internally in a map from stringified value to index; the resulting index is then used to retrieve a value from the range. Thus, an ordinal scale’s values must be coercible to a string, and the stringified version of the domain value uniquely identifies the corresponding range value. If *domain* is not specified, this method returns the current domain.
Expand Down
20 changes: 15 additions & 5 deletions src/ordinal.js
Expand Up @@ -3,25 +3,26 @@ import {slice} from "./array";

export var implicit = {name: "implicit"};

export default function ordinal(range) {
export default function ordinal() {
var index = map(),
inverseIndex = null,
domain = [],
range = [],
unknown = implicit;

range = range == null ? [] : slice.call(range);

function scale(d) {
var key = d + "", i = index.get(key);
if (!i) {
if (unknown !== implicit) return unknown;
index.set(key, i = domain.push(d));
inverseIndex = null;
}
return range[(i - 1) % range.length];
}

scale.domain = function(_) {
if (!arguments.length) return domain.slice();
domain = [], index = map();
domain = [], index = map(), inverseIndex = null;
var i = -1, n = _.length, d, key;
while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d));
return scale;
Expand All @@ -35,6 +36,15 @@ export default function ordinal(range) {
return arguments.length ? (unknown = _, scale) : unknown;
};

scale.invert = function(_) {
if (!inverseIndex) {
inverseIndex = map();
var n = domain.length;
while (--n >= 0) inverseIndex.set(range[n], n+1);
}
return domain[inverseIndex.get(_) - 1];
};

scale.copy = function() {
return ordinal()
.domain(domain)
Expand All @@ -43,4 +53,4 @@ export default function ordinal(range) {
};

return scale;
}
}
10 changes: 10 additions & 0 deletions test/ordinal-test.js
Expand Up @@ -202,3 +202,13 @@ tape("ordinal.copy() changes to the range are isolated", function(test) {
test.deepEqual(s2.range(), ["foo", "baz"]);
test.end();
});

tape("ordinal.invert(x) returns first matching domain value", function(test) {
var s = scale.scaleOrdinal().domain(["foo", "bar", "baz", "oof"]).range(["a", "b", "", "a"]);
test.equal(s.invert(), undefined);
test.equal(s.invert("a"), "foo");
test.equal(s.invert("b"), "bar");
test.equal(s.invert(""), "baz");
test.equal(s.invert("c"), undefined);
test.end();
});

0 comments on commit 67c7a58

Please sign in to comment.