From c73d8947077792715258a1ed44a5ee77335e3df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 21 May 2019 12:10:42 +0200 Subject: [PATCH 1/2] least+leastIndex with accessor (https://github.com/d3/d3-array/issues/109) --- README.md | 8 ++++++-- src/least.js | 32 +++++++++++++++++++++++--------- src/leastIndex.js | 33 ++++++++++++++++++++++++--------- test/least-test.js | 7 +++++++ test/leastIndex-test.js | 7 +++++++ 5 files changed, 67 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 2e34072e..72443fcd 100644 --- a/README.md +++ b/README.md @@ -127,25 +127,29 @@ Returns the standard deviation, defined as the square root of the [bias-correcte Methods for searching arrays for a specific element. # d3.least(iterable[, comparator]) · [Source](https://github.com/d3/d3-array/blob/master/src/least.js), [Examples](https://observablehq.com/@d3/d3-least) +
# d3.least(iterable[, accessor]) -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: +Returns the least element of the specified *iterable* according to the specified *comparator* or *accessor*. 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.least(array, (a, b) => a.foo - b.foo); // {foo: 42} d3.least(array, (a, b) => b.foo - a.foo); // {foo: 91} +d3.least(array, a => a.foo); // {foo: 42} ``` 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), [Examples](https://observablehq.com/@d3/d3-least) +
# d3.leastIndex(iterable[, comparator]) -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: +Returns the index of the least element of the specified *iterable* according to the specified *comparator* or *accessor*. 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 +d3.leastIndex(array, a => a.foo); // 0 ``` This function is similar to [minIndex](#minIndex), except it allows the use of a comparator rather than an accessor. diff --git a/src/least.js b/src/least.js index 53d470bc..db7944f5 100644 --- a/src/least.js +++ b/src/least.js @@ -1,14 +1,28 @@ 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; +export default function least(values, f = ascending) { + const valueof = f.length === 1 ? f : undefined, + compare = f.length === 2 ? f : ascending; + let min, minv, v, defined = false; + if (valueof === undefined) { + for (const value of values) { + if (defined + ? compare(value, min) < 0 + : compare(value, value) === 0) { + min = value; + defined = true; + } + } + } else { + for (const value of values) { + v = valueof(value); + if (defined + ? compare(v, minv) < 0 + : compare(v, v) === 0) { + min = value; + minv = v; + defined = true; + } } } return min; diff --git a/src/leastIndex.js b/src/leastIndex.js index 3abf9058..6a2b8f24 100644 --- a/src/leastIndex.js +++ b/src/leastIndex.js @@ -1,16 +1,31 @@ import ascending from "./ascending.js"; -export default function leastIndex(values, compare = ascending) { - let min; +export default function leastIndex(values, f = ascending) { + const valueof = f.length === 1 ? f : undefined, + compare = f.length === 2 ? f : ascending; + let min, v; 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; + if (valueof === undefined) { + for (const value of values) { + ++index; + if (minIndex < 0 + ? compare(value, value) === 0 + : compare(value, min) < 0) { + min = value; + minIndex = index; + } + } + } else { + for (const value of values) { + ++index; + v = valueof(value); + if (minIndex < 0 + ? compare(v, v) === 0 + : compare(v, min) < 0) { + min = v; + minIndex = index; + } } } return minIndex; diff --git a/test/least-test.js b/test/least-test.js index 531c78b7..34407df5 100644 --- a/test/least-test.js +++ b/test/least-test.js @@ -28,6 +28,13 @@ tape("least(array, compare) compares using the specified compare function", func test.end(); }); +tape("least(array, accessor) uses the specified accessor function", function(test) { + var a = {name: "a", v: 42}, b = {name: "b", v: 0.42}; + test.deepEqual(arrays.least([a, b], d => d.name), a); + test.deepEqual(arrays.least([a, b], d => d.v), b); + test.end(); +}); + tape("least(array) returns undefined if the array is empty", function(test) { test.strictEqual(arrays.least([]), undefined); test.end(); diff --git a/test/leastIndex-test.js b/test/leastIndex-test.js index 472fcb03..407cb606 100644 --- a/test/leastIndex-test.js +++ b/test/leastIndex-test.js @@ -28,6 +28,13 @@ tape("leastIndex(array, compare) compares using the specified compare function", test.end(); }); +tape("leastIndex(array, accessor) uses the specified accessor function", function(test) { + var a = {name: "a", v: 42}, b = {name: "b", v: 0.42}; + test.deepEqual(arrays.leastIndex([a, b], d => d.name), 0); + test.deepEqual(arrays.leastIndex([a, b], d => d.v), 1); + test.end(); +}); + tape("leastIndex(array) returns -1 if the array is empty", function(test) { test.strictEqual(arrays.leastIndex([]), -1); test.end(); From d70d2f0b4a18e0c93d81042ae20e7113c9402bfa Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 21 May 2019 10:36:40 -0700 Subject: [PATCH 2/2] Style. --- src/least.js | 26 +++++++++++++------------- src/leastIndex.js | 22 ++++++++++------------ 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/least.js b/src/least.js index db7944f5..a756abf4 100644 --- a/src/least.js +++ b/src/least.js @@ -1,26 +1,26 @@ import ascending from "./ascending.js"; -export default function least(values, f = ascending) { - const valueof = f.length === 1 ? f : undefined, - compare = f.length === 2 ? f : ascending; - let min, minv, v, defined = false; - if (valueof === undefined) { - for (const value of values) { +export default function least(values, compare = ascending) { + let min; + let defined = false; + if (compare.length === 1) { + let minValue; + for (const element of values) { + const value = compare(element); if (defined - ? compare(value, min) < 0 - : compare(value, value) === 0) { - min = value; + ? ascending(value, minValue) < 0 + : ascending(value, value) === 0) { + min = element; + minValue = value; defined = true; } } } else { for (const value of values) { - v = valueof(value); if (defined - ? compare(v, minv) < 0 - : compare(v, v) === 0) { + ? compare(value, min) < 0 + : compare(value, value) === 0) { min = value; - minv = v; defined = true; } } diff --git a/src/leastIndex.js b/src/leastIndex.js index 6a2b8f24..2cd7a931 100644 --- a/src/leastIndex.js +++ b/src/leastIndex.js @@ -1,17 +1,16 @@ import ascending from "./ascending.js"; -export default function leastIndex(values, f = ascending) { - const valueof = f.length === 1 ? f : undefined, - compare = f.length === 2 ? f : ascending; - let min, v; +export default function leastIndex(values, compare = ascending) { + let min; let minIndex = -1; let index = -1; - if (valueof === undefined) { - for (const value of values) { + if (compare.length === 1) { + for (const element of values) { ++index; + const value = compare(element); if (minIndex < 0 - ? compare(value, value) === 0 - : compare(value, min) < 0) { + ? ascending(value, value) === 0 + : ascending(value, min) < 0) { min = value; minIndex = index; } @@ -19,11 +18,10 @@ export default function leastIndex(values, f = ascending) { } else { for (const value of values) { ++index; - v = valueof(value); if (minIndex < 0 - ? compare(v, v) === 0 - : compare(v, min) < 0) { - min = v; + ? compare(value, value) === 0 + : compare(value, min) < 0) { + min = value; minIndex = index; } }