Skip to content

Commit

Permalink
Don't string-coerce ordinal domain values.
Browse files Browse the repository at this point in the history
Fixes #274. String coerce for uniqueness, but store the original input types in
the domain for subsequent retrieval. This way, you can more easily use
non-strings as domain values (such as dates or numbers).
  • Loading branch information
mbostock committed Aug 28, 2011
1 parent cd135d5 commit 874c6f4
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 41 deletions.
33 changes: 16 additions & 17 deletions d3.js
Original file line number Diff line number Diff line change
Expand Up @@ -2368,23 +2368,24 @@ d3.scale.sqrt = function() {
return d3.scale.pow().exponent(.5);
};
d3.scale.ordinal = function() {
return d3_scale_ordinal({}, 0, {t: "range", x: []});
return d3_scale_ordinal([], {t: "range", x: []});
};

function d3_scale_ordinal(domain, size, ranger) {
var range,
function d3_scale_ordinal(domain, ranger) {
var index,
range,
rangeBand;

function scale(x) {
return range[((domain[x] || (domain[x] = ++size)) - 1) % range.length];
return range[((index[x] || (index[x] = domain.push(x))) - 1) % range.length];
}

scale.domain = function(x) {
if (!arguments.length) return d3.keys(domain);
domain = {};
size = 0;
if (!arguments.length) return domain;
domain = [];
index = {};
var i = -1, n = x.length, xi;
while (++i < n) if (!domain[xi = x[i]]) domain[xi] = ++size;
while (++i < n) if (!index[xi = x[i]]) index[xi] = domain.push(xi);
return scale[ranger.t](ranger.x, ranger.p);
};

Expand All @@ -2400,8 +2401,8 @@ function d3_scale_ordinal(domain, size, ranger) {
if (arguments.length < 2) padding = 0;
var start = x[0],
stop = x[1],
step = (stop - start) / (size - 1 + padding);
range = size < 2 ? [(start + stop) / 2] : d3.range(start + step * padding / 2, stop + step / 2, step);
step = (stop - start) / (domain.length - 1 + padding);
range = domain.length < 2 ? [(start + stop) / 2] : d3.range(start + step * padding / 2, stop + step / 2, step);
rangeBand = 0;
ranger = {t: "rangePoints", x: x, p: padding};
return scale;
Expand All @@ -2411,7 +2412,7 @@ function d3_scale_ordinal(domain, size, ranger) {
if (arguments.length < 2) padding = 0;
var start = x[0],
stop = x[1],
step = (stop - start) / (size + padding);
step = (stop - start) / (domain.length + padding);
range = d3.range(start + step * padding, stop, step);
rangeBand = step * (1 - padding);
ranger = {t: "rangeBands", x: x, p: padding};
Expand All @@ -2422,8 +2423,8 @@ function d3_scale_ordinal(domain, size, ranger) {
if (arguments.length < 2) padding = 0;
var start = x[0],
stop = x[1],
step = Math.floor((stop - start) / (size + padding)),
err = stop - start - (size - padding) * step;
step = Math.floor((stop - start) / (domain.length + padding)),
err = stop - start - (domain.length - padding) * step;
range = d3.range(start + Math.round(err / 2), stop, step);
rangeBand = Math.round(step * (1 - padding));
ranger = {t: "rangeRoundBands", x: x, p: padding};
Expand All @@ -2435,12 +2436,10 @@ function d3_scale_ordinal(domain, size, ranger) {
};

scale.copy = function() {
var copy = {}, x;
for (x in domain) copy[x] = domain[x];
return d3_scale_ordinal(copy, size, ranger);
return d3_scale_ordinal(domain, ranger);
};

return scale[ranger.t](ranger.x, ranger.p);
return scale.domain(domain);
};
/*
* This product includes color specifications and designs developed by Cynthia
Expand Down
4 changes: 2 additions & 2 deletions d3.min.js

Large diffs are not rendered by default.

33 changes: 16 additions & 17 deletions src/scale/ordinal.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
d3.scale.ordinal = function() {
return d3_scale_ordinal({}, 0, {t: "range", x: []});
return d3_scale_ordinal([], {t: "range", x: []});
};

function d3_scale_ordinal(domain, size, ranger) {
var range,
function d3_scale_ordinal(domain, ranger) {
var index,
range,
rangeBand;

function scale(x) {
return range[((domain[x] || (domain[x] = ++size)) - 1) % range.length];
return range[((index[x] || (index[x] = domain.push(x))) - 1) % range.length];
}

scale.domain = function(x) {
if (!arguments.length) return d3.keys(domain);
domain = {};
size = 0;
if (!arguments.length) return domain;
domain = [];
index = {};
var i = -1, n = x.length, xi;
while (++i < n) if (!domain[xi = x[i]]) domain[xi] = ++size;
while (++i < n) if (!index[xi = x[i]]) index[xi] = domain.push(xi);
return scale[ranger.t](ranger.x, ranger.p);
};

Expand All @@ -31,8 +32,8 @@ function d3_scale_ordinal(domain, size, ranger) {
if (arguments.length < 2) padding = 0;
var start = x[0],
stop = x[1],
step = (stop - start) / (size - 1 + padding);
range = size < 2 ? [(start + stop) / 2] : d3.range(start + step * padding / 2, stop + step / 2, step);
step = (stop - start) / (domain.length - 1 + padding);
range = domain.length < 2 ? [(start + stop) / 2] : d3.range(start + step * padding / 2, stop + step / 2, step);
rangeBand = 0;
ranger = {t: "rangePoints", x: x, p: padding};
return scale;
Expand All @@ -42,7 +43,7 @@ function d3_scale_ordinal(domain, size, ranger) {
if (arguments.length < 2) padding = 0;
var start = x[0],
stop = x[1],
step = (stop - start) / (size + padding);
step = (stop - start) / (domain.length + padding);
range = d3.range(start + step * padding, stop, step);
rangeBand = step * (1 - padding);
ranger = {t: "rangeBands", x: x, p: padding};
Expand All @@ -53,8 +54,8 @@ function d3_scale_ordinal(domain, size, ranger) {
if (arguments.length < 2) padding = 0;
var start = x[0],
stop = x[1],
step = Math.floor((stop - start) / (size + padding)),
err = stop - start - (size - padding) * step;
step = Math.floor((stop - start) / (domain.length + padding)),
err = stop - start - (domain.length - padding) * step;
range = d3.range(start + Math.round(err / 2), stop, step);
rangeBand = Math.round(step * (1 - padding));
ranger = {t: "rangeRoundBands", x: x, p: padding};
Expand All @@ -66,10 +67,8 @@ function d3_scale_ordinal(domain, size, ranger) {
};

scale.copy = function() {
var copy = {}, x;
for (x in domain) copy[x] = domain[x];
return d3_scale_ordinal(copy, size, ranger);
return d3_scale_ordinal(domain, ranger);
};

return scale[ranger.t](ranger.x, ranger.p);
return scale.domain(domain);
};
32 changes: 27 additions & 5 deletions test/scale/ordinal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,39 @@ suite.addBatch({
var x = ordinal().range(["foo", "bar"]);
assert.equal(x(1), "foo");
assert.equal(x(0), "bar");
assert.deepEqual(x.domain(), ["0", "1"]);
assert.deepEqual(x.domain(), [1, 0]);
x.domain(["0", "1"]);
assert.equal(x(0), "foo"); // it changed!
assert.equal(x(1), "bar");
assert.deepEqual(x.domain(), ["0", "1"]);
},
"coerces domain values to strings": function(ordinal) {
"uniqueness is based on string coercion": function(ordinal) {
var x = ordinal().domain(["foo"]).range([42, 43, 44]);
assert.equal(x(new String("foo")), 42);
assert.equal(x({toString: function() { return "foo"; }}), 42);
assert.equal(x({toString: function() { return "bar"; }}), 43);
},
"orders domain values by the order in which they are seen": function(ordinal) {
var x = ordinal();
x("foo");
x("bar");
x("baz");
assert.deepEqual(x.domain(), ["foo", "bar", "baz"]);
x.domain(["baz", "bar"]);
x("foo");
assert.deepEqual(x.domain(), ["baz", "bar", "foo"]);
x.domain(["baz", "foo"]);
assert.deepEqual(x.domain(), ["baz", "foo"]);
x.domain([]);
x("foo");
x("bar");
assert.deepEqual(x.domain(), ["foo", "bar"]);
},
"does not coerce domain values to strings": function(ordinal) {
var x = ordinal().domain([0, 1]);
assert.deepEqual(x.domain(), ["0", "1"]);
assert.typeOf(x.domain()[0], "string");
assert.typeOf(x.domain()[1], "string");
assert.deepEqual(x.domain(), [0, 1]);
assert.typeOf(x.domain()[0], "number");
assert.typeOf(x.domain()[1], "number");
}
},

Expand Down

0 comments on commit 874c6f4

Please sign in to comment.