From fc33498c3327459ac31f671753f8341c5cc78542 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Sun, 28 Mar 2021 16:47:39 -0700 Subject: [PATCH 1/4] treat null as undefined --- src/continuous.js | 2 +- test/linear-test.js | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/continuous.js b/src/continuous.js index 2dd1cf9..b92797a 100644 --- a/src/continuous.js +++ b/src/continuous.js @@ -83,7 +83,7 @@ export function transformer() { } function scale(x) { - return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x))); + return x === null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x))); } scale.invert = function(y) { diff --git a/test/linear-test.js b/test/linear-test.js index 59b815f..1c655be 100644 --- a/test/linear-test.js +++ b/test/linear-test.js @@ -209,8 +209,9 @@ tape("linear.rangeRound(range) accepts an iterable", function(test) { test.end(); }); -tape("linear.unknown(value) sets the return value for undefined and NaN input", function(test) { +tape("linear.unknown(value) sets the return value for undefined, null, and NaN input", function(test) { var s = scale.scaleLinear().unknown(-1); + test.equals(s(null), -1); test.equal(s(undefined), -1); test.equal(s(NaN), -1); test.equal(s("N/A"), -1); @@ -388,15 +389,12 @@ tape("linear.ticks(X) spans linear.nice(X).domain()", function(test) { var ticks = s.ticks(count); test.deepEqual([ticks[0], ticks[ticks.length - 1]], s.domain()); } - check([1, 9], 2); check([1, 9], 3); check([1, 9], 4); - check([8, 9], 2); check([8, 9], 3); check([8, 9], 4); - check([1, 21], 2); check([2, 21], 2); check([3, 21], 2); @@ -408,7 +406,6 @@ tape("linear.ticks(X) spans linear.nice(X).domain()", function(test) { check([9, 21], 2); check([10, 21], 2); check([11, 21], 2); - test.end(); }) From e6236850e18adbd84d7f2b3df22568763ff25509 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 29 Mar 2021 09:01:02 -0700 Subject: [PATCH 2/4] undefined isNaN --- src/continuous.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/continuous.js b/src/continuous.js index b92797a..d932095 100644 --- a/src/continuous.js +++ b/src/continuous.js @@ -83,7 +83,7 @@ export function transformer() { } function scale(x) { - return x === null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x))); + return x == null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x))); } scale.invert = function(y) { From a70f2e3c0abed447bbb98bcdafd1a10cfa8cbe4f Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 29 Mar 2021 09:04:11 -0700 Subject: [PATCH 3/4] treat null as undefined --- src/identity.js | 2 +- src/quantile.js | 2 +- src/quantize.js | 2 +- src/sequential.js | 2 +- src/sequentialQuantile.js | 2 +- src/threshold.js | 2 +- test/threshold-test.js | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/identity.js b/src/identity.js index af41c59..e1d1da7 100644 --- a/src/identity.js +++ b/src/identity.js @@ -5,7 +5,7 @@ export default function identity(domain) { var unknown; function scale(x) { - return isNaN(x = +x) ? unknown : x; + return x == null || isNaN(x = +x) ? unknown : x; } scale.invert = scale; diff --git a/src/quantile.js b/src/quantile.js index b4e708c..303f935 100644 --- a/src/quantile.js +++ b/src/quantile.js @@ -15,7 +15,7 @@ export default function quantile() { } function scale(x) { - return isNaN(x = +x) ? unknown : range[bisect(thresholds, x)]; + return x == null || isNaN(x = +x) ? unknown : range[bisect(thresholds, x)]; } scale.invertExtent = function(y) { diff --git a/src/quantize.js b/src/quantize.js index 735a531..0f54003 100644 --- a/src/quantize.js +++ b/src/quantize.js @@ -11,7 +11,7 @@ export default function quantize() { unknown; function scale(x) { - return x <= x ? range[bisect(domain, x, 0, n)] : unknown; + return x != null && x <= x ? range[bisect(domain, x, 0, n)] : unknown; } function rescale() { diff --git a/src/sequential.js b/src/sequential.js index bd38dba..547dc40 100644 --- a/src/sequential.js +++ b/src/sequential.js @@ -18,7 +18,7 @@ function transformer() { unknown; function scale(x) { - return isNaN(x = +x) ? unknown : interpolator(k10 === 0 ? 0.5 : (x = (transform(x) - t0) * k10, clamp ? Math.max(0, Math.min(1, x)) : x)); + return x == null || isNaN(x = +x) ? unknown : interpolator(k10 === 0 ? 0.5 : (x = (transform(x) - t0) * k10, clamp ? Math.max(0, Math.min(1, x)) : x)); } scale.domain = function(_) { diff --git a/src/sequentialQuantile.js b/src/sequentialQuantile.js index 132ebd0..7437e55 100644 --- a/src/sequentialQuantile.js +++ b/src/sequentialQuantile.js @@ -7,7 +7,7 @@ export default function sequentialQuantile() { interpolator = identity; function scale(x) { - if (!isNaN(x = +x)) return interpolator((bisect(domain, x, 1) - 1) / (domain.length - 1)); + if (x != null && !isNaN(x = +x)) return interpolator((bisect(domain, x, 1) - 1) / (domain.length - 1)); } scale.domain = function(_) { diff --git a/src/threshold.js b/src/threshold.js index 79d3559..bbdbe39 100644 --- a/src/threshold.js +++ b/src/threshold.js @@ -8,7 +8,7 @@ export default function threshold() { n = 1; function scale(x) { - return x <= x ? range[bisect(domain, x, 0, n)] : unknown; + return x != null && x <= x ? range[bisect(domain, x, 0, n)] : unknown; } scale.domain = function(_) { diff --git a/test/threshold-test.js b/test/threshold-test.js index 7499bf8..a6c1fcf 100644 --- a/test/threshold-test.js +++ b/test/threshold-test.js @@ -26,7 +26,7 @@ tape("threshold(x) returns undefined if the specified value x is not orderable", test.equal(x(), undefined); test.equal(x(undefined), undefined); test.equal(x(NaN), undefined); - test.equal(x(null), "a"); // null < 1/3 + test.equal(x(null), undefined); test.end(); }); From febe68886c063653a0cdf2a8f57b17e93b2591c5 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 29 Mar 2021 09:30:17 -0700 Subject: [PATCH 4/4] more tests --- test/identity-test.js | 10 ++++++++++ test/linear-test.js | 2 +- test/quantile-test.js | 3 ++- test/quantize-test.js | 8 ++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/test/identity-test.js b/test/identity-test.js index 4e1ea3c..53a1213 100644 --- a/test/identity-test.js +++ b/test/identity-test.js @@ -31,6 +31,16 @@ tape("identity(x) coerces input to a number", function(test) { test.end(); }); +tape("identity(undefined) returns unknown", function(test) { + var s = scale.scaleIdentity().unknown(-1); + test.equal(s(undefined), -1); + test.equal(s(null), -1); + test.equal(s(NaN), -1); + test.equal(s("N/A"), -1); + test.equal(s(0.4), 0.4); + test.end(); +}); + tape("identity.invert(y) is the identity function", function(test) { var s = scale.scaleIdentity().domain([1, 2]); test.equal(s.invert(0.5), 0.5); diff --git a/test/linear-test.js b/test/linear-test.js index 1c655be..a862d6e 100644 --- a/test/linear-test.js +++ b/test/linear-test.js @@ -211,7 +211,7 @@ tape("linear.rangeRound(range) accepts an iterable", function(test) { tape("linear.unknown(value) sets the return value for undefined, null, and NaN input", function(test) { var s = scale.scaleLinear().unknown(-1); - test.equals(s(null), -1); + test.equal(s(null), -1); test.equal(s(undefined), -1); test.equal(s(NaN), -1); test.equal(s("N/A"), -1); diff --git a/test/quantile-test.js b/test/quantile-test.js index 14b8a76..b88b46e 100644 --- a/test/quantile-test.js +++ b/test/quantile-test.js @@ -129,9 +129,10 @@ tape("quantile.invertExtent() returns the first match if duplicate values exist test.end(); }); -tape("quantile.unknown(value) sets the return value for undefined and NaN input", function(test) { +tape("quantile.unknown(value) sets the return value for undefined, null, and NaN input", function(test) { var s = scale.scaleQuantile().domain([3, 6, 7, 8, 8, 10, 13, 15, 16, 20]).range([0, 1, 2, 3]).unknown(-1); test.equal(s(undefined), -1); + test.equal(s(null), -1); test.equal(s(NaN), -1); test.equal(s("N/A"), -1); test.equal(s(2), 0); diff --git a/test/quantize-test.js b/test/quantize-test.js index 53295ba..fc51978 100644 --- a/test/quantize-test.js +++ b/test/quantize-test.js @@ -36,6 +36,14 @@ tape("quantize(value) clamps input values to the domain", function(test) { test.end(); }); +tape("quantize.unknown(value) sets the return value for undefined, null, and NaN input", function(test) { + var s = scale.scaleQuantize().range([0, 1, 2]).unknown(-1); + test.equal(s(undefined), -1); + test.equal(s(null), -1); + test.equal(s(NaN), -1); + test.end(); +}); + tape("quantize.domain() coerces domain values to numbers", function(test) { var s = scale.scaleQuantize().domain(["-1.20", "2.40"]); test.deepEqual(s.domain(), [-1.2, 2.4]);