Skip to content

Commit 4ba9d54

Browse files
authored
optimize channel type coercion (#863)
1 parent 8e30e22 commit 4ba9d54

File tree

3 files changed

+38
-12
lines changed

3 files changed

+38
-12
lines changed

src/channel.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {ascending, descending, rollup, sort} from "d3";
2-
import {first, labelof, maybeValue, range, valueof} from "./options.js";
2+
import {first, labelof, map, maybeValue, range, valueof} from "./options.js";
33
import {registry} from "./scales/index.js";
44
import {maybeReduce} from "./transforms/group.js";
55

@@ -52,7 +52,7 @@ export function channelSort(channels, facetChannels, data, options) {
5252
function difference(channels, k1, k2) {
5353
const X1 = values(channels, k1);
5454
const X2 = values(channels, k2);
55-
return Float64Array.from(X2, (x2, i) => Math.abs(x2 - X1[i]));
55+
return map(X2, (x2, i) => Math.abs(x2 - X1[i]), Float64Array);
5656
}
5757

5858
function values(channels, name, alias) {

src/options.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ export function arrayify(data, type) {
8080
: (data instanceof type ? data : type.from(data)));
8181
}
8282

83+
// An optimization of type.from(values, f): if the given values are already an
84+
// instanceof the desired array type, the faster values.map method is used.
85+
export function map(values, f, type = Array) {
86+
return values instanceof type ? values.map(f) : type.from(values, f);
87+
}
88+
89+
export function isTypedArray(values) {
90+
return values instanceof TypedArray;
91+
}
92+
8393
// Disambiguates an options object (e.g., {y: "x2"}) from a primitive value.
8494
export function isObject(option) {
8595
return option?.toString === objectToString;

src/scales.js

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {parse as isoParse} from "isoformat";
2-
import {isColor, isEvery, isOrdinal, isFirst, isSymbol, isTemporal, maybeSymbol, order, isTemporalString, isNumericString, isScaleOptions} from "./options.js";
2+
import {isColor, isEvery, isOrdinal, isFirst, isSymbol, isTemporal, isTemporalString, isNumericString, isScaleOptions, isTypedArray, map, maybeSymbol, order} from "./options.js";
33
import {registry, color, position, radius, opacity, symbol, length} from "./scales/index.js";
44
import {ScaleLinear, ScaleSqrt, ScalePow, ScaleLog, ScaleSymlog, ScaleQuantile, ScaleQuantize, ScaleThreshold, ScaleIdentity} from "./scales/quantitative.js";
55
import {ScaleDiverging, ScaleDivergingSqrt, ScaleDivergingPow, ScaleDivergingLog, ScaleDivergingSymlog} from "./scales/diverging.js";
@@ -171,17 +171,17 @@ function Scale(key, channels = [], options = {}) {
171171
case "pow":
172172
case "log":
173173
case "symlog":
174-
options = coerceType(channels, options, coerceNumber, Float64Array);
174+
options = coerceType(channels, options, coerceNumbers);
175175
break;
176176
case "identity":
177177
switch (registry.get(key)) {
178-
case position: options = coerceType(channels, options, coerceNumber, Float64Array); break;
179-
case symbol: options = coerceType(channels, options, maybeSymbol); break;
178+
case position: options = coerceType(channels, options, coerceNumbers); break;
179+
case symbol: options = coerceType(channels, options, coerceSymbols); break;
180180
}
181181
break;
182182
case "utc":
183183
case "time":
184-
options = coerceType(channels, options, coerceDate);
184+
options = coerceType(channels, options, coerceDates);
185185
break;
186186
}
187187

@@ -352,13 +352,29 @@ export function isCollapsed(scale) {
352352
}
353353

354354
// Mutates channel.value!
355-
function coerceType(channels, options, coerce, type) {
356-
for (const c of channels) c.value = coerceArray(c.value, coerce, type);
357-
return {...options, domain: coerceArray(options.domain, coerce, type)};
355+
function coerceType(channels, {domain, ...options}, coerceValues) {
356+
for (const c of channels) {
357+
if (c.value !== undefined) {
358+
c.value = coerceValues(c.value);
359+
}
360+
}
361+
return {
362+
domain: domain === undefined ? domain : coerceValues(domain),
363+
...options
364+
};
365+
}
366+
367+
function coerceSymbols(values) {
368+
return map(values, maybeSymbol);
369+
}
370+
371+
function coerceDates(values) {
372+
return map(values, coerceDate);
358373
}
359374

360-
function coerceArray(array, coerce, type = Array) {
361-
if (array !== undefined) return type.from(array, coerce);
375+
// If the values are specified as a typed array, no coercion is required.
376+
function coerceNumbers(values) {
377+
return isTypedArray(values) ? values : map(values, coerceNumber, Float64Array);
362378
}
363379

364380
// Unlike Mark’s number, here we want to convert null and undefined to NaN,

0 commit comments

Comments
 (0)