Array manipulation, ordering, searching, summarizing, etc.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Functions for generating ranges, tick values, and other numeric sequences commonly used in scales and axes. These utilities are essential for creating evenly-spaced values and "nice" numbers for data visualization.
Returns an array containing an arithmetic progression, similar to Python's built-in range.
/**
* Returns an array containing an arithmetic progression
* @param start - Starting value (defaults to 0 if omitted)
* @param stop - Ending value (exclusive)
* @param step - Step size (defaults to 1)
* @returns Array of numbers in arithmetic progression
*/
function range(start?: number, stop: number, step?: number): number[];
function range(stop: number): number[];Usage Examples:
import { range } from "d3-array";
// Basic usage
range(5); // [0, 1, 2, 3, 4]
range(2, 8); // [2, 3, 4, 5, 6, 7]
range(0, 10, 2); // [0, 2, 4, 6, 8]
// Negative step
range(10, 0, -1); // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
// Fractional step
range(0, 1, 0.2); // [0, 0.2, 0.4, 0.6000000000000001, 0.8]
// Empty range (invalid parameters)
range(5, 2); // []
range(0, 0); // []Practical Examples:
import { range } from "d3-array";
// Generate array indices
const data = ['a', 'b', 'c', 'd'];
const indices = range(data.length); // [0, 1, 2, 3]
// Create evenly spaced values for interpolation
const steps = range(0, 1, 1/10); // [0, 0.1, 0.2, ..., 0.9]
// Generate coordinates for a grid
const gridX = range(0, 100, 10); // [0, 10, 20, ..., 90]
const gridY = range(0, 50, 5); // [0, 5, 10, ..., 45]Functions for generating "nice" tick values for axes and scales.
Returns an array of approximately count + 1 uniformly-spaced, nicely-rounded values.
/**
* Returns nicely-rounded, uniformly-spaced tick values
* @param start - Starting value (inclusive)
* @param stop - Ending value (inclusive)
* @param count - Approximate number of intervals (actual ticks = count + 1)
* @returns Array of nice tick values
*/
function ticks(start: number, stop: number, count: number): number[];Usage Examples:
import { ticks } from "d3-array";
ticks(0, 10, 5); // [0, 2, 4, 6, 8, 10] (6 ticks, 5 intervals)
ticks(0, 1, 10); // [0.0, 0.1, 0.2, ..., 1.0] (11 ticks)
ticks(-5, 5, 5); // [-4, -2, 0, 2, 4] (5 ticks)
// Handles edge cases gracefully
ticks(0, 0, 5); // [0] (single point)
ticks(10, 0, 5); // [] (invalid range)Returns the difference between adjacent tick values if the same arguments were passed to ticks.
/**
* Returns the step size between adjacent tick values
* @param start - Starting value
* @param stop - Ending value
* @param count - Approximate number of intervals
* @returns The step size between ticks
*/
function tickStep(start: number, stop: number, count: number): number;Usage Examples:
import { tickStep } from "d3-array";
tickStep(0, 10, 5); // 2 (ticks at 0, 2, 4, 6, 8, 10)
tickStep(0, 1, 10); // 0.1 (ticks at 0.0, 0.1, 0.2, etc.)
tickStep(0, 100, 4); // 25 (ticks at 0, 25, 50, 75, 100)Like tickStep, but guarantees that the result is an integer when the step would be less than one.
/**
* Like tickStep, but returns negative inverse if step would be < 1
* @param start - Starting value (must be <= stop)
* @param stop - Ending value (must be >= start)
* @param count - Approximate number of intervals
* @returns Integer increment (positive or negative)
*/
function tickIncrement(start: number, stop: number, count: number): number;Usage Examples:
import { tickIncrement, tickStep } from "d3-array";
// When step >= 1, same as tickStep
tickIncrement(0, 10, 5); // 2
tickStep(0, 10, 5); // 2
// When step < 1, returns negative inverse
tickIncrement(0, 1, 10); // -10 (instead of 0.1)
tickStep(0, 1, 10); // 0.1
// This ensures integer arithmetic for tick generation
tickIncrement(0, 0.5, 10); // -20 (instead of 0.05)Returns a new interval [niceStart, niceStop] that covers the given interval with nice round numbers.
/**
* Returns a nice interval covering the given range
* @param start - Starting value (must be <= stop)
* @param stop - Ending value (must be >= start)
* @param count - Approximate number of intervals for computing nice values
* @returns [niceStart, niceStop] array with rounded endpoints
*/
function nice(start: number, stop: number, count: number): [number, number];Usage Examples:
import { nice } from "d3-array";
nice(1.3, 8.7, 5); // [0, 10] (extends range to nice round numbers)
nice(0.17, 0.91, 4); // [0, 1] (rounds to unit interval)
nice(23, 87, 3); // [0, 100] (extends to century boundaries)
// Useful for chart axis bounds
const dataRange = [12.3, 97.8];
const niceRange = nice(dataRange[0], dataRange[1], 8);
console.log(niceRange); // [10, 100] or similar nice boundariesimport { extent, ticks, nice } from "d3-array";
function createAxisTicks(data, accessor, tickCount = 8) {
// Get data range
const [min, max] = extent(data, accessor);
// Extend to nice boundaries
const [niceMin, niceMax] = nice(min, max, tickCount);
// Generate nice tick values
const tickValues = ticks(niceMin, niceMax, tickCount);
return {
domain: [niceMin, niceMax],
ticks: tickValues,
step: tickValues[1] - tickValues[0]
};
}
// Usage with data
const salesData = [
{month: 'Jan', sales: 12300},
{month: 'Feb', sales: 15600},
{month: 'Mar', sales: 18900},
// ... more data
];
const yAxis = createAxisTicks(salesData, d => d.sales);
console.log(yAxis);
// {
// domain: [10000, 20000],
// ticks: [10000, 12000, 14000, 16000, 18000, 20000],
// step: 2000
// }import { range } from "d3-array";
function createAnimationFrames(duration, fps = 60) {
const totalFrames = Math.ceil(duration * fps / 1000);
return range(totalFrames).map(frame => ({
frame,
time: (frame / totalFrames) * duration,
progress: frame / (totalFrames - 1)
}));
}
// 2-second animation at 60fps
const frames = createAnimationFrames(2000);
frames.forEach(({frame, time, progress}) => {
console.log(`Frame ${frame}: ${time}ms (${(progress * 100).toFixed(1)}%)`);
});import { range, ticks } from "d3-array";
function generateGrid(width, height, gridSize) {
// Create grid lines at regular intervals
const verticalLines = range(0, width + 1, gridSize);
const horizontalLines = range(0, height + 1, gridSize);
return {
vertical: verticalLines.map(x => ({x1: x, y1: 0, x2: x, y2: height})),
horizontal: horizontalLines.map(y => ({x1: 0, y1: y, x2: width, y2: y}))
};
}
// Alternative with nice grid spacing
function generateNiceGrid(width, height, approximateGridCount = 10) {
const xTicks = ticks(0, width, approximateGridCount);
const yTicks = ticks(0, height, approximateGridCount);
return {
vertical: xTicks.map(x => ({x1: x, y1: 0, x2: x, y2: height})),
horizontal: yTicks.map(y => ({x1: 0, y1: y, x2: width, y2: y}))
};
}import { extent, nice, ticks } from "d3-array";
class LinearScale {
constructor(domain, range) {
this.domain(domain);
this.range(range);
}
domain(domain) {
if (!domain) return this._domain;
this._domain = [...domain];
return this;
}
range(range) {
if (!range) return this._range;
this._range = [...range];
return this;
}
// Create nice domain
nice(count = 10) {
const [min, max] = this._domain;
this._domain = nice(min, max, count);
return this;
}
// Generate tick values
ticks(count = 10) {
const [min, max] = this._domain;
return ticks(min, max, count);
}
// Map value from domain to range
scale(value) {
const [d0, d1] = this._domain;
const [r0, r1] = this._range;
return r0 + (value - d0) * (r1 - r0) / (d1 - d0);
}
}
// Usage
const data = [2.1, 15.7, 8.3, 12.9, 6.4];
const [min, max] = extent(data);
const scale = new LinearScale([min, max], [0, 100])
.nice(5); // Make domain boundaries nice
console.log('Domain:', scale.domain()); // [0, 20] (nice boundaries)
console.log('Ticks:', scale.ticks(5)); // [0, 5, 10, 15, 20]
// Map data values to screen coordinates
data.forEach(value => {
console.log(`${value} -> ${scale.scale(value)}`);
});import { range } from "d3-array";
function generateColorSteps(startColor, endColor, steps) {
return range(steps).map(i => {
const t = i / (steps - 1);
return interpolateColor(startColor, endColor, t);
});
}
function interpolateColor(color1, color2, t) {
// Simple RGB interpolation
const r1 = parseInt(color1.slice(1, 3), 16);
const g1 = parseInt(color1.slice(3, 5), 16);
const b1 = parseInt(color1.slice(5, 7), 16);
const r2 = parseInt(color2.slice(1, 3), 16);
const g2 = parseInt(color2.slice(3, 5), 16);
const b2 = parseInt(color2.slice(5, 7), 16);
const r = Math.round(r1 + (r2 - r1) * t);
const g = Math.round(g1 + (g2 - g1) * t);
const b = Math.round(b1 + (b2 - b1) * t);
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
}
// Generate gradient colors
const gradient = generateColorSteps('#ff0000', '#0000ff', 10);
console.log(gradient); // ['#ff0000', '#e6001c', ..., '#0000ff']import { range } from "d3-array";
function logRange(start, stop, base = 10) {
const logStart = Math.log(start) / Math.log(base);
const logStop = Math.log(stop) / Math.log(base);
const steps = Math.floor(logStop - logStart) + 1;
return range(steps).map(i => Math.pow(base, logStart + i));
}
// Generate powers of 10
const powersOf10 = logRange(1, 1000);
console.log(powersOf10); // [1, 10, 100, 1000]
// Generate powers of 2
const powersOf2 = logRange(1, 64, 2);
console.log(powersOf2); // [1, 2, 4, 8, 16, 32, 64]Install with Tessl CLI
npx tessl i tessl/npm-d3-array