diff --git a/README.md b/README.md
index bc717f3d..f8d22398 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,13 @@ Returns the minimum value in the given *iterable* using natural order. If the it
Unlike the built-in [Math.min](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/min), this method ignores undefined, null and NaN values; this is useful for ignoring missing data. In addition, elements are compared using natural order rather than numeric order. For example, the minimum of the strings [“20”, “3”] is “20”, while the minimum of the numbers [20, 3] is 3.
-See also [scan](#scan) and [extent](#extent).
+See also [extent](#extent).
+
+# d3.minIndex(iterable[, accessor]) · [Source](https://github.com/d3/d3-array/blob/master/src/minIndex.js)
+
+Returns the index of the minimum value in the given *iterable* using natural order. If the iterable contains no comparable values, returns -1. An optional *accessor* function may be specified, which is equivalent to calling Array.from before computing the minimum value.
+
+Unlike the built-in [Math.min](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/min), this method ignores undefined, null and NaN values; this is useful for ignoring missing data. In addition, elements are compared using natural order rather than numeric order. For example, the minimum of the strings [“20”, “3”] is “20”, while the minimum of the numbers [20, 3] is 3.
# d3.max(iterable[, accessor]) · [Source](https://github.com/d3/d3-array/blob/master/src/max.js)
@@ -68,7 +74,13 @@ Returns the maximum value in the given *iterable* using natural order. If the it
Unlike the built-in [Math.max](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/max), this method ignores undefined values; this is useful for ignoring missing data. In addition, elements are compared using natural order rather than numeric order. For example, the maximum of the strings [“20”, “3”] is “3”, while the maximum of the numbers [20, 3] is 20.
-See also [scan](#scan) and [extent](#extent).
+See also [extent](#extent).
+
+# d3.maxIndex(iterable[, accessor]) · [Source](https://github.com/d3/d3-array/blob/master/src/maxIndex.js)
+
+Returns the index of the maximum value in the given *iterable* using natural order. If the iterable contains no comparable values, returns -1. An optional *accessor* function may be specified, which is equivalent to calling Array.from before computing the maximum value.
+
+Unlike the built-in [Math.max](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/max), this method ignores undefined values; this is useful for ignoring missing data. In addition, elements are compared using natural order rather than numeric order. For example, the maximum of the strings [“20”, “3”] is “3”, while the maximum of the numbers [20, 3] is 20.
# d3.extent(iterable[, accessor]) · [Source](https://github.com/d3/d3-array/blob/master/src/extent.js)
@@ -114,17 +126,33 @@ Returns the standard deviation, defined as the square root of the [bias-correcte
Methods for searching arrays for a specific element.
-# d3.scan(iterable[, comparator]) · [Source](https://github.com/d3/d3-array/blob/master/src/scan.js)
+# d3.least(iterable[, comparator]) · [Source](https://github.com/d3/d3-array/blob/master/src/least.js)
-Performs a linear scan of the specified *iterable*, returning the index of the least element according to the specified *comparator*. If the given *iterable* contains no comparable elements (*i.e.*, the comparator returns NaN when comparing each element to itself), returns undefined. If *comparator* is not specified, it defaults to [ascending](#ascending). For example:
+Returns the least element of the specified *iterable* according to the specified *comparator*. If the given *iterable* contains no comparable elements (*i.e.*, the comparator returns NaN when comparing each element to itself), returns undefined. If *comparator* is not specified, it defaults to [ascending](#ascending). For example:
```js
const array = [{foo: 42}, {foo: 91}];
-d3.scan(array, (a, b) => a.foo - b.foo); // 0
-d3.scan(array, (a, b) => b.foo - a.foo); // 1
+d3.least(array, (a, b) => a.foo - b.foo); // {foo: 42}
+d3.least(array, (a, b) => b.foo - a.foo); // {foo: 91}
```
-This function is similar to [min](#min), except it allows the use of a comparator rather than an accessor and it returns the index instead of the accessed value. See also [bisect](#bisect).
+This function is similar to [min](#min), except it allows the use of a comparator rather than an accessor.
+
+# d3.leastIndex(iterable[, comparator]) · [Source](https://github.com/d3/d3-array/blob/master/src/leastIndex.js)
+
+Returns the index of the least element of the specified *iterable* according to the specified *comparator*. If the given *iterable* contains no comparable elements (*i.e.*, the comparator returns NaN when comparing each element to itself), returns -1. If *comparator* is not specified, it defaults to [ascending](#ascending). For example:
+
+```js
+const array = [{foo: 42}, {foo: 91}];
+d3.leastIndex(array, (a, b) => a.foo - b.foo); // 0
+d3.leastIndex(array, (a, b) => b.foo - a.foo); // 1
+```
+
+This function is similar to [minIndex](#minIndex), except it allows the use of a comparator rather than an accessor.
+
+# d3.scan(iterable[, comparator]) · [Source](https://github.com/d3/d3-array/blob/master/src/scan.js)
+
+Deprecated; use [leastIndex](#leastIndex) instead.
# d3.bisectLeft(array, x[, lo[, hi]]) · [Source](https://github.com/d3/d3-array/blob/master/src/bisect.js)
diff --git a/src/extent.js b/src/extent.js
index 44402daf..2e3738d6 100644
--- a/src/extent.js
+++ b/src/extent.js
@@ -2,10 +2,10 @@ export default function(values, valueof) {
let min;
let max;
if (valueof === undefined) {
- for (let value of values) {
- if (value != null && value >= value) {
+ for (const value of values) {
+ if (value != null) {
if (min === undefined) {
- min = max = value;
+ if (value >= value) min = max = value;
} else {
if (min > value) min = value;
if (max < value) max = value;
@@ -15,9 +15,9 @@ export default function(values, valueof) {
} else {
let index = -1;
for (let value of values) {
- if ((value = valueof(value, ++index, values)) != null && value >= value) {
+ if ((value = valueof(value, ++index, values)) != null) {
if (min === undefined) {
- min = max = value;
+ if (value >= value) min = max = value;
} else {
if (min > value) min = value;
if (max < value) max = value;
diff --git a/src/index.js b/src/index.js
index 927a6625..004ea8e8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -6,22 +6,26 @@ export {default as descending} from "./descending.js";
export {default as deviation} from "./deviation.js";
export {default as extent} from "./extent.js";
export {default as group} from "./group.js";
-export {default as bin, default as histogram} from "./bin.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";
export {default as thresholdSturges} from "./threshold/sturges.js";
export {default as max} from "./max.js";
+export {default as maxIndex} from "./maxIndex.js";
export {default as mean} from "./mean.js";
export {default as median} from "./median.js";
export {default as merge} from "./merge.js";
export {default as min} from "./min.js";
+export {default as minIndex} from "./minIndex.js";
export {default as pairs} from "./pairs.js";
export {default as permute} from "./permute.js";
export {default as quantile} from "./quantile.js";
export {default as quickselect} from "./quickselect.js";
export {default as range} from "./range.js";
export {default as rollup} from "./rollup.js";
-export {default as scan} from "./scan.js";
+export {default as least} from "./least.js";
+export {default as leastIndex} from "./leastIndex.js";
+export {default as scan} from "./scan.js"; // Deprecated; use leastIndex.
export {default as shuffle} from "./shuffle.js";
export {default as sum} from "./sum.js";
export {default as ticks, tickIncrement, tickStep} from "./ticks.js";
diff --git a/src/least.js b/src/least.js
new file mode 100644
index 00000000..53d470bc
--- /dev/null
+++ b/src/least.js
@@ -0,0 +1,15 @@
+import ascending from "./ascending.js";
+
+export default function least(values, compare = ascending) {
+ let min;
+ let defined = false;
+ for (const value of values) {
+ if (defined
+ ? compare(value, min) < 0
+ : compare(value, value) === 0) {
+ min = value;
+ defined = true;
+ }
+ }
+ return min;
+}
diff --git a/src/leastIndex.js b/src/leastIndex.js
new file mode 100644
index 00000000..3abf9058
--- /dev/null
+++ b/src/leastIndex.js
@@ -0,0 +1,17 @@
+import ascending from "./ascending.js";
+
+export default function leastIndex(values, compare = ascending) {
+ let min;
+ let minIndex = -1;
+ let index = -1;
+ for (const value of values) {
+ ++index;
+ if (minIndex < 0
+ ? compare(value, value) === 0
+ : compare(value, min) < 0) {
+ min = value;
+ minIndex = index;
+ }
+ }
+ return minIndex;
+}
diff --git a/src/max.js b/src/max.js
index 4ff90e58..ce287368 100644
--- a/src/max.js
+++ b/src/max.js
@@ -1,10 +1,9 @@
export default function max(values, valueof) {
let max;
if (valueof === undefined) {
- for (let value of values) {
+ for (const value of values) {
if (value != null
- && value >= value
- && (max === undefined || max < value)) {
+ && (max < value || (max === undefined && value >= value))) {
max = value;
}
}
@@ -12,8 +11,7 @@ export default function max(values, valueof) {
let index = -1;
for (let value of values) {
if ((value = valueof(value, ++index, values)) != null
- && value >= value
- && (max === undefined || max < value)) {
+ && (max < value || (max === undefined && value >= value))) {
max = value;
}
}
diff --git a/src/maxIndex.js b/src/maxIndex.js
new file mode 100644
index 00000000..87da1a2c
--- /dev/null
+++ b/src/maxIndex.js
@@ -0,0 +1,22 @@
+export default function maxIndex(values, valueof) {
+ let max;
+ let maxIndex = -1;
+ let index = -1;
+ if (valueof === undefined) {
+ for (const value of values) {
+ ++index;
+ if (value != null
+ && (max < value || (max === undefined && value >= value))) {
+ max = value, maxIndex = index;
+ }
+ }
+ } else {
+ for (let value of values) {
+ if ((value = valueof(value, ++index, values)) != null
+ && (max < value || (max === undefined && value >= value))) {
+ max = value, maxIndex = index;
+ }
+ }
+ }
+ return maxIndex;
+}
diff --git a/src/min.js b/src/min.js
index 018417ed..df88bfb2 100644
--- a/src/min.js
+++ b/src/min.js
@@ -1,10 +1,9 @@
export default function min(values, valueof) {
let min;
if (valueof === undefined) {
- for (let value of values) {
+ for (const value of values) {
if (value != null
- && value >= value
- && (min === undefined || min > value)) {
+ && (min > value || (min === undefined && value >= value))) {
min = value;
}
}
@@ -12,8 +11,7 @@ export default function min(values, valueof) {
let index = -1;
for (let value of values) {
if ((value = valueof(value, ++index, values)) != null
- && value >= value
- && (min === undefined || min > value)) {
+ && (min > value || (min === undefined && value >= value))) {
min = value;
}
}
diff --git a/src/minIndex.js b/src/minIndex.js
new file mode 100644
index 00000000..5c07d1ea
--- /dev/null
+++ b/src/minIndex.js
@@ -0,0 +1,22 @@
+export default function minIndex(values, valueof) {
+ let min;
+ let minIndex = -1;
+ let index = -1;
+ if (valueof === undefined) {
+ for (const value of values) {
+ ++index;
+ if (value != null
+ && (min > value || (min === undefined && value >= value))) {
+ min = value, minIndex = index;
+ }
+ }
+ } else {
+ for (let value of values) {
+ if ((value = valueof(value, ++index, values)) != null
+ && (min > value || (min === undefined && value >= value))) {
+ min = value, minIndex = index;
+ }
+ }
+ }
+ return minIndex;
+}
diff --git a/src/scan.js b/src/scan.js
index f965311d..9c538f8a 100644
--- a/src/scan.js
+++ b/src/scan.js
@@ -1,17 +1,6 @@
-import ascending from "./ascending.js";
+import leastIndex from "./leastIndex.js";
-export default function scan(values, compare = ascending) {
- let min;
- let minIndex;
- let index = -1;
- for (const value of values) {
- ++index;
- if (minIndex === undefined
- ? compare(value, value) === 0
- : compare(value, min) < 0) {
- min = value;
- minIndex = index;
- }
- }
- return minIndex;
+export default function scan(values, compare) {
+ const index = leastIndex(values, compare);
+ return index < 0 ? undefined : index;
}
diff --git a/test/least-test.js b/test/least-test.js
new file mode 100644
index 00000000..531c78b7
--- /dev/null
+++ b/test/least-test.js
@@ -0,0 +1,58 @@
+var tape = require("tape"),
+ arrays = require("../");
+
+tape("least(array) compares using natural order", function(test) {
+ test.strictEqual(arrays.least([0, 1]), 0);
+ test.strictEqual(arrays.least([1, 0]), 0);
+ test.strictEqual(arrays.least([0, "1"]), 0);
+ test.strictEqual(arrays.least(["1", 0]), 0);
+ test.strictEqual(arrays.least(["10", "2"]), "10");
+ test.strictEqual(arrays.least(["2", "10"]), "10");
+ test.strictEqual(arrays.least(["10", "2", NaN]), "10");
+ test.strictEqual(arrays.least([NaN, "10", "2"]), "10");
+ test.strictEqual(arrays.least(["2", NaN, "10"]), "10");
+ test.strictEqual(arrays.least([2, NaN, 10]), 2);
+ test.strictEqual(arrays.least([10, 2, NaN]), 2);
+ test.strictEqual(arrays.least([NaN, 10, 2]), 2);
+ test.end();
+});
+
+tape("least(array, compare) compares using the specified compare function", function(test) {
+ var a = {name: "a"}, b = {name: "b"};
+ test.deepEqual(arrays.least([a, b], function(a, b) { return a.name.localeCompare(b.name); }), {name: "a"});
+ test.strictEqual(arrays.least([1, 0], arrays.descending), 1);
+ test.strictEqual(arrays.least(["1", 0], arrays.descending), "1");
+ test.strictEqual(arrays.least(["2", "10"], arrays.descending), "2");
+ test.strictEqual(arrays.least(["2", NaN, "10"], arrays.descending), "2");
+ test.strictEqual(arrays.least([2, NaN, 10], arrays.descending), 10);
+ test.end();
+});
+
+tape("least(array) returns undefined if the array is empty", function(test) {
+ test.strictEqual(arrays.least([]), undefined);
+ test.end();
+});
+
+tape("least(array) returns undefined if the array contains only incomparable values", function(test) {
+ test.strictEqual(arrays.least([NaN, undefined]), undefined);
+ test.strictEqual(arrays.least([NaN, "foo"], function(a, b) { return a - b; }), undefined);
+ test.end();
+});
+
+tape("least(array) returns the first of equal values", function(test) {
+ test.deepEqual(arrays.least([2, 2, 1, 1, 0, 0, 0, 3, 0].map(box), ascendingValue), {value: 0, index: 4});
+ test.deepEqual(arrays.least([3, 2, 2, 1, 1, 0, 0, 0, 3, 0].map(box), descendingValue), {value: 3, index: 0});
+ test.end();
+});
+
+function box(value, index) {
+ return {value, index};
+}
+
+function ascendingValue(a, b) {
+ return a.value - b.value;
+}
+
+function descendingValue(a, b) {
+ return b.value - a.value;
+}
diff --git a/test/leastIndex-test.js b/test/leastIndex-test.js
new file mode 100644
index 00000000..472fcb03
--- /dev/null
+++ b/test/leastIndex-test.js
@@ -0,0 +1,46 @@
+var tape = require("tape"),
+ arrays = require("../");
+
+tape("leastIndex(array) compares using natural order", function(test) {
+ test.strictEqual(arrays.leastIndex([0, 1]), 0);
+ test.strictEqual(arrays.leastIndex([1, 0]), 1);
+ test.strictEqual(arrays.leastIndex([0, "1"]), 0);
+ test.strictEqual(arrays.leastIndex(["1", 0]), 1);
+ test.strictEqual(arrays.leastIndex(["10", "2"]), 0);
+ test.strictEqual(arrays.leastIndex(["2", "10"]), 1);
+ test.strictEqual(arrays.leastIndex(["10", "2", NaN]), 0);
+ test.strictEqual(arrays.leastIndex([NaN, "10", "2"]), 1);
+ test.strictEqual(arrays.leastIndex(["2", NaN, "10"]), 2);
+ test.strictEqual(arrays.leastIndex([2, NaN, 10]), 0);
+ test.strictEqual(arrays.leastIndex([10, 2, NaN]), 1);
+ test.strictEqual(arrays.leastIndex([NaN, 10, 2]), 2);
+ test.end();
+});
+
+tape("leastIndex(array, compare) compares using the specified compare function", function(test) {
+ var a = {name: "a"}, b = {name: "b"};
+ test.strictEqual(arrays.leastIndex([a, b], function(a, b) { return a.name.localeCompare(b.name); }), 0);
+ test.strictEqual(arrays.leastIndex([1, 0], arrays.descending), 0);
+ test.strictEqual(arrays.leastIndex(["1", 0], arrays.descending), 0);
+ test.strictEqual(arrays.leastIndex(["2", "10"], arrays.descending), 0);
+ test.strictEqual(arrays.leastIndex(["2", NaN, "10"], arrays.descending), 0);
+ test.strictEqual(arrays.leastIndex([2, NaN, 10], arrays.descending), 2);
+ test.end();
+});
+
+tape("leastIndex(array) returns -1 if the array is empty", function(test) {
+ test.strictEqual(arrays.leastIndex([]), -1);
+ test.end();
+});
+
+tape("leastIndex(array) returns -1 if the array contains only incomparable values", function(test) {
+ test.strictEqual(arrays.leastIndex([NaN, undefined]), -1);
+ test.strictEqual(arrays.leastIndex([NaN, "foo"], function(a, b) { return a - b; }), -1);
+ test.end();
+});
+
+tape("leastIndex(array) returns the first of equal values", function(test) {
+ test.strictEqual(arrays.leastIndex([2, 2, 1, 1, 0, 0, 0, 3, 0]), 4);
+ test.strictEqual(arrays.leastIndex([3, 2, 2, 1, 1, 0, 0, 0, 3, 0], arrays.descending), 0);
+ test.end();
+});
diff --git a/test/maxIndex-test.js b/test/maxIndex-test.js
new file mode 100644
index 00000000..f91b7371
--- /dev/null
+++ b/test/maxIndex-test.js
@@ -0,0 +1,110 @@
+var tape = require("tape"),
+ arrays = require("../");
+
+tape("maxIndex(array) returns the index of the greatest numeric value for numbers", function(test) {
+ test.deepEqual(arrays.maxIndex([1]), 0);
+ test.deepEqual(arrays.maxIndex([5, 1, 2, 3, 4]), 0);
+ test.deepEqual(arrays.maxIndex([20, 3]), 0);
+ test.deepEqual(arrays.maxIndex([3, 20]), 1);
+ test.end();
+});
+
+tape("maxIndex(array) returns the greatest lexicographic value for strings", function(test) {
+ test.deepEqual(arrays.maxIndex(["c", "a", "b"]), 0);
+ test.deepEqual(arrays.maxIndex(["20", "3"]), 1);
+ test.deepEqual(arrays.maxIndex(["3", "20"]), 0);
+ test.end();
+});
+
+tape("maxIndex(array) ignores null, undefined and NaN", function(test) {
+ var o = {valueOf: function() { return NaN; }};
+ test.deepEqual(arrays.maxIndex([NaN, 1, 2, 3, 4, 5]), 5);
+ test.deepEqual(arrays.maxIndex([o, 1, 2, 3, 4, 5]), 5);
+ test.deepEqual(arrays.maxIndex([1, 2, 3, 4, 5, NaN]), 4);
+ test.deepEqual(arrays.maxIndex([1, 2, 3, 4, 5, o]), 4);
+ test.deepEqual(arrays.maxIndex([10, null, 3, undefined, 5, NaN]), 0);
+ test.deepEqual(arrays.maxIndex([-1, null, -3, undefined, -5, NaN]), 0);
+ test.end();
+});
+
+tape("maxIndex(array) compares heterogenous types as numbers", function(test) {
+ test.equal(arrays.maxIndex([20, "3"]), 0);
+ test.equal(arrays.maxIndex(["20", 3]), 0);
+ test.equal(arrays.maxIndex([3, "20"]), 1);
+ test.equal(arrays.maxIndex(["3", 20]), 1);
+ test.end();
+});
+
+tape("maxIndex(array) returns -1 if the array contains no numbers", function(test) {
+ test.equal(arrays.maxIndex([]), -1);
+ test.equal(arrays.maxIndex([null]), -1);
+ test.equal(arrays.maxIndex([undefined]), -1);
+ test.equal(arrays.maxIndex([NaN]), -1);
+ test.equal(arrays.maxIndex([NaN, NaN]), -1);
+ test.end();
+});
+
+tape("maxIndex(array, f) returns the greatest numeric value for numbers", function(test) {
+ test.deepEqual(arrays.maxIndex([1].map(box), unbox), 0);
+ test.deepEqual(arrays.maxIndex([5, 1, 2, 3, 4].map(box), unbox), 0);
+ test.deepEqual(arrays.maxIndex([20, 3].map(box), unbox), 0);
+ test.deepEqual(arrays.maxIndex([3, 20].map(box), unbox), 1);
+ test.end();
+});
+
+tape("maxIndex(array, f) returns the greatest lexicographic value for strings", function(test) {
+ test.deepEqual(arrays.maxIndex(["c", "a", "b"].map(box), unbox), 0);
+ test.deepEqual(arrays.maxIndex(["20", "3"].map(box), unbox), 1);
+ test.deepEqual(arrays.maxIndex(["3", "20"].map(box), unbox), 0);
+ test.end();
+});
+
+tape("maxIndex(array, f) ignores null, undefined and NaN", function(test) {
+ var o = {valueOf: function() { return NaN; }};
+ test.deepEqual(arrays.maxIndex([NaN, 1, 2, 3, 4, 5].map(box), unbox), 5);
+ test.deepEqual(arrays.maxIndex([o, 1, 2, 3, 4, 5].map(box), unbox), 5);
+ test.deepEqual(arrays.maxIndex([1, 2, 3, 4, 5, NaN].map(box), unbox), 4);
+ test.deepEqual(arrays.maxIndex([1, 2, 3, 4, 5, o].map(box), unbox), 4);
+ test.deepEqual(arrays.maxIndex([10, null, 3, undefined, 5, NaN].map(box), unbox), 0);
+ test.deepEqual(arrays.maxIndex([-1, null, -3, undefined, -5, NaN].map(box), unbox), 0);
+ test.end();
+});
+
+tape("maxIndex(array, f) compares heterogenous types as numbers", function(test) {
+ test.equal(arrays.maxIndex([20, "3"].map(box), unbox), 0);
+ test.equal(arrays.maxIndex(["20", 3].map(box), unbox), 0);
+ test.equal(arrays.maxIndex([3, "20"].map(box), unbox), 1);
+ test.equal(arrays.maxIndex(["3", 20].map(box), unbox), 1);
+ test.end();
+});
+
+tape("maxIndex(array, f) returns -1 if the array contains no observed values", function(test) {
+ test.equal(arrays.maxIndex([].map(box), unbox), -1);
+ test.equal(arrays.maxIndex([null].map(box), unbox), -1);
+ test.equal(arrays.maxIndex([undefined].map(box), unbox), -1);
+ test.equal(arrays.maxIndex([NaN].map(box), unbox), -1);
+ test.equal(arrays.maxIndex([NaN, NaN].map(box), unbox), -1);
+ test.end();
+});
+
+tape("maxIndex(array, f) passes the accessor d, i, and array", function(test) {
+ var results = [], array = ["a", "b", "c"];
+ arrays.maxIndex(array, function(d, i, array) { results.push([d, i, array]); });
+ test.deepEqual(results, [["a", 0, array], ["b", 1, array], ["c", 2, array]]);
+ test.end();
+});
+
+tape("maxIndex(array, f) uses the global context", function(test) {
+ var results = [];
+ arrays.maxIndex([1, 2], function() { results.push(this); });
+ test.deepEqual(results, [global, global]);
+ test.end();
+});
+
+function box(value) {
+ return {value: value};
+}
+
+function unbox(box) {
+ return box.value;
+}
diff --git a/test/minIndex-test.js b/test/minIndex-test.js
new file mode 100644
index 00000000..bbc84bdc
--- /dev/null
+++ b/test/minIndex-test.js
@@ -0,0 +1,110 @@
+var tape = require("tape"),
+ arrays = require("../");
+
+tape("minIndex(array) returns the index of the least numeric value for numbers", function(test) {
+ test.deepEqual(arrays.minIndex([1]), 0);
+ test.deepEqual(arrays.minIndex([5, 1, 2, 3, 4]), 1);
+ test.deepEqual(arrays.minIndex([20, 3]), 1);
+ test.deepEqual(arrays.minIndex([3, 20]), 0);
+ test.end();
+});
+
+tape("minIndex(array) returns the index of the least lexicographic value for strings", function(test) {
+ test.deepEqual(arrays.minIndex(["c", "a", "b"]), 1);
+ test.deepEqual(arrays.minIndex(["20", "3"]), 0);
+ test.deepEqual(arrays.minIndex(["3", "20"]), 1);
+ test.end();
+});
+
+tape("minIndex(array) ignores null, undefined and NaN", function(test) {
+ var o = {valueOf: function() { return NaN; }};
+ test.deepEqual(arrays.minIndex([NaN, 1, 2, 3, 4, 5]), 1);
+ test.deepEqual(arrays.minIndex([o, 1, 2, 3, 4, 5]), 1);
+ test.deepEqual(arrays.minIndex([1, 2, 3, 4, 5, NaN]), 0);
+ test.deepEqual(arrays.minIndex([1, 2, 3, 4, 5, o]), 0);
+ test.deepEqual(arrays.minIndex([10, null, 3, undefined, 5, NaN]), 2);
+ test.deepEqual(arrays.minIndex([-1, null, -3, undefined, -5, NaN]), 4);
+ test.end();
+});
+
+tape("minIndex(array) compares heterogenous types as numbers", function(test) {
+ test.equal(arrays.minIndex([20, "3"]), 1);
+ test.equal(arrays.minIndex(["20", 3]), 1);
+ test.equal(arrays.minIndex([3, "20"]), 0);
+ test.equal(arrays.minIndex(["3", 20]), 0);
+ test.end();
+});
+
+tape("minIndex(array) returns -1 if the array contains no numbers", function(test) {
+ test.equal(arrays.minIndex([]), -1);
+ test.equal(arrays.minIndex([null]), -1);
+ test.equal(arrays.minIndex([undefined]), -1);
+ test.equal(arrays.minIndex([NaN]), -1);
+ test.equal(arrays.minIndex([NaN, NaN]), -1);
+ test.end();
+});
+
+tape("minIndex(array, f) returns the index of the least numeric value for numbers", function(test) {
+ test.deepEqual(arrays.minIndex([1].map(box), unbox), 0);
+ test.deepEqual(arrays.minIndex([5, 1, 2, 3, 4].map(box), unbox), 1);
+ test.deepEqual(arrays.minIndex([20, 3].map(box), unbox), 1);
+ test.deepEqual(arrays.minIndex([3, 20].map(box), unbox), 0);
+ test.end();
+});
+
+tape("minIndex(array, f) returns the index of the least lexicographic value for strings", function(test) {
+ test.deepEqual(arrays.minIndex(["c", "a", "b"].map(box), unbox), 1);
+ test.deepEqual(arrays.minIndex(["20", "3"].map(box), unbox), 0);
+ test.deepEqual(arrays.minIndex(["3", "20"].map(box), unbox), 1);
+ test.end();
+});
+
+tape("minIndex(array, f) ignores null, undefined and NaN", function(test) {
+ var o = {valueOf: function() { return NaN; }};
+ test.deepEqual(arrays.minIndex([NaN, 1, 2, 3, 4, 5].map(box), unbox), 1);
+ test.deepEqual(arrays.minIndex([o, 1, 2, 3, 4, 5].map(box), unbox), 1);
+ test.deepEqual(arrays.minIndex([1, 2, 3, 4, 5, NaN].map(box), unbox), 0);
+ test.deepEqual(arrays.minIndex([1, 2, 3, 4, 5, o].map(box), unbox), 0);
+ test.deepEqual(arrays.minIndex([10, null, 3, undefined, 5, NaN].map(box), unbox), 2);
+ test.deepEqual(arrays.minIndex([-1, null, -3, undefined, -5, NaN].map(box), unbox), 4);
+ test.end();
+});
+
+tape("minIndex(array, f) compares heterogenous types as numbers", function(test) {
+ test.equal(arrays.minIndex([20, "3"].map(box), unbox), 1);
+ test.equal(arrays.minIndex(["20", 3].map(box), unbox), 1);
+ test.equal(arrays.minIndex([3, "20"].map(box), unbox), 0);
+ test.equal(arrays.minIndex(["3", 20].map(box), unbox), 0);
+ test.end();
+});
+
+tape("minIndex(array, f) returns -1 if the array contains no observed values", function(test) {
+ test.equal(arrays.minIndex([].map(box), unbox), -1);
+ test.equal(arrays.minIndex([null].map(box), unbox), -1);
+ test.equal(arrays.minIndex([undefined].map(box), unbox), -1);
+ test.equal(arrays.minIndex([NaN].map(box), unbox), -1);
+ test.equal(arrays.minIndex([NaN, NaN].map(box), unbox), -1);
+ test.end();
+});
+
+tape("minIndex(array, f) passes the accessor d, i, and array", function(test) {
+ var results = [], array = ["a", "b", "c"];
+ arrays.minIndex(array, function(d, i, array) { results.push([d, i, array]); });
+ test.deepEqual(results, [["a", 0, array], ["b", 1, array], ["c", 2, array]]);
+ test.end();
+});
+
+tape("minIndex(array, f) uses the global context", function(test) {
+ var results = [];
+ arrays.minIndex([1, 2], function() { results.push(this); });
+ test.deepEqual(results, [global, global]);
+ test.end();
+});
+
+function box(value) {
+ return {value: value};
+}
+
+function unbox(box) {
+ return box.value;
+}