From bec0354560f0c07feabd912cee518d3433cc5b80 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Sat, 3 Dec 2022 09:51:48 -0800 Subject: [PATCH] =?UTF-8?q?fix=20#263;=20don=E2=80=99t=20mutate=20user=20t?= =?UTF-8?q?hresholds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin.js | 10 ++++++---- test/bin-test.js | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/bin.js b/src/bin.js index 28d64aad..6f179ad4 100644 --- a/src/bin.js +++ b/src/bin.js @@ -66,9 +66,11 @@ export default function bin() { } // Remove any thresholds outside the domain. - var m = tz.length; - while (tz[0] <= x0) tz.shift(), --m; - while (tz[m - 1] > x1) tz.pop(), --m; + // Be careful not to mutate an array owned by the user! + var m = tz.length, a = 0, b = m; + while (tz[a] <= x0) ++a; + while (tz[b - 1] > x1) --b; + if (a || (b < m)) tz = tz.slice(a, b), m = b - a; var bins = new Array(m + 1), bin; @@ -116,7 +118,7 @@ export default function bin() { }; histogram.thresholds = function(_) { - return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), histogram) : threshold; + return arguments.length ? (threshold = typeof _ === "function" ? _ : constant(Array.isArray(_) ? slice.call(_) : _), histogram) : threshold; }; return histogram; diff --git a/test/bin-test.js b/test/bin-test.js index 389e505d..e6453b2d 100644 --- a/test/bin-test.js +++ b/test/bin-test.js @@ -241,6 +241,22 @@ it("bin(data) assigns integer values to the correct bins", () => { assert.deepStrictEqual(bin().domain([3, 8])(eights), [box([], 3, 4), box([], 4, 5), box([], 5, 6), box([], 6, 7), box(eights, 7, 8)]); }); +it("bin(data) does not mutate user-supplied thresholds as an array", () => { + const thresholds = [3, 4, 5, 6]; + const b = bin().domain([4, 5]).thresholds(thresholds); + assert.deepStrictEqual(b([5]), [box([], 4, 5), box([5], 5, 5)]); + assert.deepStrictEqual(thresholds, [3, 4, 5, 6]); + assert.deepStrictEqual(b.thresholds()(), [3, 4, 5, 6]); +}); + +it("bin(data) does not mutate user-supplied thresholds as a function", () => { + const thresholds = [3, 4, 5, 6]; + const b = bin().domain([4, 5]).thresholds(() => thresholds); + assert.deepStrictEqual(b([5]), [box([], 4, 5), box([5], 5, 5)]); + assert.deepStrictEqual(thresholds, [3, 4, 5, 6]); + assert.deepStrictEqual(b.thresholds()(), [3, 4, 5, 6]); +}); + function box(bin, x0, x1) { bin.x0 = x0; bin.x1 = x1;