Permalink
Browse files

Fix d3.min and d3.max, again.

Unlike Math.min and Math.max, it doesn't make sense to return negative or
positive infinity for d3.min and d3.max; the D3 functions return the minimum
value according to an arbitrary ordering, not by numeric value. Instead, the
minimum or maximum of an empty array, or an array that contains only degenerate
values, should always be undefined.
  • Loading branch information...
1 parent 204e00e commit 2e560f6d6e020b471deb63116345221e7564eb22 @mbostock mbostock committed May 30, 2011
Showing with 126 additions and 66 deletions.
  1. +7 −3 d3.js
  2. +2 −2 d3.min.js
  3. +1 −1 src/core/core.js
  4. +3 −1 src/core/max.js
  5. +3 −1 src/core/min.js
  6. +30 −17 tests/test-max.js
  7. +25 −12 tests/test-max.out
  8. +30 −17 tests/test-min.js
  9. +25 −12 tests/test-min.out
View
10 d3.js
@@ -1,4 +1,4 @@
-(function(){d3 = {version: "1.19.0"}; // semver
+(function(){d3 = {version: "1.19.1"}; // semver
if (!Date.now) Date.now = function() {
return +new Date;
};
@@ -43,23 +43,27 @@ d3.descending = function(a, b) {
d3.min = function(array, f) {
var i = -1,
n = array.length,
- a = Infinity,
+ a,
b;
if (arguments.length === 1) {
+ while (++i < n && ((a = array[i]) == null || a != a)) a = undefined;
while (++i < n) if ((b = array[i]) != null && a > b) a = b;
} else {
+ while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined;
while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
}
return a;
};
d3.max = function(array, f) {
var i = -1,
n = array.length,
- a = -Infinity,
+ a,
b;
if (arguments.length === 1) {
+ while (++i < n && ((a = array[i]) == null || a != a)) a = undefined;
while (++i < n) if ((b = array[i]) != null && b > a) a = b;
} else {
+ while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined;
while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
}
return a;
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -1 +1 @@
-d3 = {version: "1.19.0"}; // semver
+d3 = {version: "1.19.1"}; // semver
View
@@ -1,11 +1,13 @@
d3.max = function(array, f) {
var i = -1,
n = array.length,
- a = -Infinity,
+ a,
b;
if (arguments.length === 1) {
+ while (++i < n && ((a = array[i]) == null || a != a)) a = undefined;
while (++i < n) if ((b = array[i]) != null && b > a) a = b;
} else {
+ while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined;
while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
}
return a;
View
@@ -1,11 +1,13 @@
d3.min = function(array, f) {
var i = -1,
n = array.length,
- a = Infinity,
+ a,
b;
if (arguments.length === 1) {
+ while (++i < n && ((a = array[i]) == null || a != a)) a = undefined;
while (++i < n) if ((b = array[i]) != null && a > b) a = b;
} else {
+ while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined;
while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
}
return a;
View
@@ -1,30 +1,43 @@
require("./../lib/env-js/envjs/node");
require("./../d3");
-console.log("max:");
-console.log(" " + d3.max([1, 2, 3, 4, 5]));
-console.log("");
+function max(array) {
+ var max = d3.max(array);
+ return typeof max === "string" ? "\"" + max + "\"" : max;
+}
-console.log("max with accessor function:");
-console.log(" " + d3.max([[1, 2, 3, 4, 5], [2, 4, 6, 8, 10]], function(d) {
- return d3.max(d);
-}));
+console.log("max:");
+console.log(" [1] -> " + max([1]))
+console.log(" [5, 1, 2, 3, 4] -> " + max([5, 1, 2, 3, 4]));
+console.log(" [\"a\", \"b\", \"c\"] -> " + max(["c", "a", "b"]))
+console.log(" [\"20\", \"3\"] -> " + max(["20", "3"]))
+console.log(" [\"3\", \"20\"] -> " + max(["3", "20"]))
+console.log(" [20, 3] -> " + max([20, 3]))
+console.log(" [3, 20] -> " + max([3, 20]))
console.log("");
-console.log("max index:");
-console.log(" " + d3.max([1, 2, 3, 4, 5], function(d, i) {
- return i;
-}));
+console.log("max of empty array is undefined:");
+console.log(" [] -> " + max([]))
+console.log(" [null] -> " + max([null]))
+console.log(" [undefined] -> " + max([undefined]))
+console.log(" [NaN] -> " + max([NaN]))
+console.log(" [NaN, NaN] -> " + max([NaN, NaN]))
console.log("");
-console.log("max with first element NaN:");
-console.log(" " + d3.max([NaN, 1, 2, 3, 4, 5]));
+console.log("max ignores null, undefined, and NaN:");
+console.log(" [NaN, 1, 2, 3, 4, 5] -> " + max([NaN, 1, 2, 3, 4, 5]));
+console.log(" [1, 2, 3, 4, 5, NaN] -> " + max([1, 2, 3, 4, 5, NaN]));
+console.log(" [10, null, 3, undefined, 3, NaN] -> " + max([10, null, 3, undefined, 5, NaN]));
console.log("");
-console.log("max with last element NaN:");
-console.log(" " + d3.max([1, 2, 3, 4, 5, NaN]));
+console.log("max compares heterogenous types as numbers:");
+console.log(" [20, \"3\"] -> " + max([20, "3"]))
+console.log(" [\"20\", 3] -> " + max(["20", 3]))
+console.log(" [3, \"20\"] -> " + max([3, "20"]))
+console.log(" [\"3\", 20] -> " + max(["3", 20]))
console.log("");
-console.log("max with null and undefined elements:");
-console.log(" " + d3.max([-5, null, -3, undefined, -10, NaN]));
+console.log("max applies accessor function:");
+console.log(" [1, 2, 3, 4, 5], [2, 4, 6, 8, 10] -> " + d3.max([[1, 2, 3, 4, 5], [2, 4, 6, 8, 10]], function(d) { return d3.min(d); }));
+console.log(" [1, 2, 3, 4, 5] -> " + d3.max([1, 2, 3, 4, 5], function(d, i) { return i; }));
console.log("");
View
@@ -1,18 +1,31 @@
max:
- 5
+ [1] -> 1
+ [5, 1, 2, 3, 4] -> 5
+ ["a", "b", "c"] -> "c"
+ ["20", "3"] -> "3"
+ ["3", "20"] -> "3"
+ [20, 3] -> 20
+ [3, 20] -> 20
-max with accessor function:
- 10
+max of empty array is undefined:
+ [] -> undefined
+ [null] -> undefined
+ [undefined] -> undefined
+ [NaN] -> undefined
+ [NaN, NaN] -> undefined
-max index:
- 4
+max ignores null, undefined, and NaN:
+ [NaN, 1, 2, 3, 4, 5] -> 5
+ [1, 2, 3, 4, 5, NaN] -> 5
+ [10, null, 3, undefined, 3, NaN] -> 10
-max with first element NaN:
- 5
+max compares heterogenous types as numbers:
+ [20, "3"] -> 20
+ ["20", 3] -> "20"
+ [3, "20"] -> "20"
+ ["3", 20] -> 20
-max with last element NaN:
- 5
-
-max with null and undefined elements:
- -3
+max applies accessor function:
+ [1, 2, 3, 4, 5], [2, 4, 6, 8, 10] -> 2
+ [1, 2, 3, 4, 5] -> 4
View
@@ -1,30 +1,43 @@
require("./../lib/env-js/envjs/node");
require("./../d3");
-console.log("min:");
-console.log(" " + d3.min([1, 2, 3, 4, 5]));
-console.log("");
+function min(array) {
+ var min = d3.min(array);
+ return typeof min === "string" ? "\"" + min + "\"" : min;
+}
-console.log("min with accessor function:");
-console.log(" " + d3.min([[1, 2, 3, 4, 5], [2, 4, 6, 8, 10]], function(d) {
- return d3.max(d);
-}));
+console.log("min:");
+console.log(" [1] -> " + min([1]))
+console.log(" [5, 1, 2, 3, 4] -> " + min([5, 1, 2, 3, 4]));
+console.log(" [\"a\", \"b\", \"c\"] -> " + min(["c", "a", "b"]))
+console.log(" [\"20\", \"3\"] -> " + min(["20", "3"]))
+console.log(" [\"3\", \"20\"] -> " + min(["3", "20"]))
+console.log(" [20, 3] -> " + min([20, 3]))
+console.log(" [3, 20] -> " + min([3, 20]))
console.log("");
-console.log("min index:");
-console.log(" " + d3.min([1, 2, 3, 4, 5], function(d, i) {
- return i;
-}));
+console.log("min of empty array is undefined:");
+console.log(" [] -> " + min([]))
+console.log(" [null] -> " + min([null]))
+console.log(" [undefined] -> " + min([undefined]))
+console.log(" [NaN] -> " + min([NaN]))
+console.log(" [NaN, NaN] -> " + min([NaN, NaN]))
console.log("");
-console.log("min with first element NaN:");
-console.log(" " + d3.min([NaN, 1, 2, 3, 4, 5]));
+console.log("min ignores null, undefined, and NaN:");
+console.log(" [NaN, 1, 2, 3, 4, 5] -> " + min([NaN, 1, 2, 3, 4, 5]));
+console.log(" [1, 2, 3, 4, 5, NaN] -> " + min([1, 2, 3, 4, 5, NaN]));
+console.log(" [10, null, 3, undefined, 3, NaN] -> " + min([10, null, 3, undefined, 5, NaN]));
console.log("");
-console.log("min with last element NaN:");
-console.log(" " + d3.min([1, 2, 3, 4, 5, NaN]));
+console.log("min compares heterogenous types as numbers:");
+console.log(" [20, \"3\"] -> " + min([20, "3"]))
+console.log(" [\"20\", 3] -> " + min(["20", 3]))
+console.log(" [3, \"20\"] -> " + min([3, "20"]))
+console.log(" [\"3\", 20] -> " + min(["3", 20]))
console.log("");
-console.log("min with null and undefined elements:");
-console.log(" " + d3.min([10, null, 3, undefined, 5, NaN]));
+console.log("min applies accessor function:");
+console.log(" [1, 2, 3, 4, 5], [2, 4, 6, 8, 10] -> " + d3.min([[1, 2, 3, 4, 5], [2, 4, 6, 8, 10]], function(d) { return d3.max(d); }));
+console.log(" [1, 2, 3, 4, 5] -> " + d3.min([1, 2, 3, 4, 5], function(d, i) { return i; }));
console.log("");
View
@@ -1,18 +1,31 @@
min:
- 1
+ [1] -> 1
+ [5, 1, 2, 3, 4] -> 1
+ ["a", "b", "c"] -> "a"
+ ["20", "3"] -> "20"
+ ["3", "20"] -> "20"
+ [20, 3] -> 3
+ [3, 20] -> 3
-min with accessor function:
- 5
+min of empty array is undefined:
+ [] -> undefined
+ [null] -> undefined
+ [undefined] -> undefined
+ [NaN] -> undefined
+ [NaN, NaN] -> undefined
-min index:
- 0
+min ignores null, undefined, and NaN:
+ [NaN, 1, 2, 3, 4, 5] -> 1
+ [1, 2, 3, 4, 5, NaN] -> 1
+ [10, null, 3, undefined, 3, NaN] -> 3
-min with first element NaN:
- 1
+min compares heterogenous types as numbers:
+ [20, "3"] -> "3"
+ ["20", 3] -> 3
+ [3, "20"] -> 3
+ ["3", 20] -> "3"
-min with last element NaN:
- 1
-
-min with null and undefined elements:
- 3
+min applies accessor function:
+ [1, 2, 3, 4, 5], [2, 4, 6, 8, 10] -> 5
+ [1, 2, 3, 4, 5] -> 0

0 comments on commit 2e560f6

Please sign in to comment.