Работа с массивами в экосистеме D3.js опирается на модуль d3-array, который предоставляет набор утилит для вычисления статистик, поиска экстремумов, сортировки и подготовки данных к визуализации. Эти функции не заменяют стандартные методы JavaScript, но дают более выразительные и композиционные инструменты для работы с данными, особенно когда используются accessors и сложные структуры объектов.
Основой сортировки в d3-array являются компараторы
d3.ascending и d3.descending. Они принимают
два значения и возвращают отрицательное, ноль или положительное число,
что соответствует контракту Array.prototype.sort.
import { ascending, descending } from "d3-array";
[3, 1, 10, 5].sort(ascending); // [1, 3, 5, 10]
[3, 1, 10, 5].sort(descending); // [10, 5, 3, 1]
Главное преимущество этих компараторов — устойчивость к
undefined и возможность использовать их как строительные
блоки для более сложных сортировок.
Часто данные представляют собой массив объектов, и сортировка выполняется по полям:
import { ascending } from "d3-array";
const data = [
{ name: "A", value: 10 },
{ name: "B", value: 3 },
{ name: "C", value: 7 }
];
data.sort((a, b) => ascending(a.value, b.value));
Accessor-функции позволяют отделить структуру данных от логики сортировки:
const byValue = (d) => d.value;
data.sort((a, b) => ascending(byValue(a), byValue(b)));
Такой подход делает сортировки переиспользуемыми и удобными в сложных визуализациях.
d3-array предоставляет функции для быстрого поиска экстремумов без необходимости полной сортировки массива.
Эти функции вычисляют максимальное или минимальное значение:
import { max, min } from "d3-array";
const values = [10, 5, 20, 3];
max(values); // 20
min(values); // 3
Для массивов объектов используется accessor:
max(data, d => d.value);
min(data, d => d.value);
Это особенно важно при построении шкал (scales), где диапазон данных задаёт область отображения.
Когда нужно не только значение, но и сам элемент:
import { greatest, least } from "d3-array";
greatest(data, (a, b) => a.value - b.value); // объект с максимальным value
least(data, (a, b) => a.value - b.value); // объект с минимальным value
Эти функции полезны, когда требуется отобразить «лучший» или «худший» элемент в визуализации.
Иногда важен не элемент, а его позиция:
import { greatestIndex, leastIndex } from "d3-array";
greatestIndex([1, 9, 3]); // 1
leastIndex([1, 9, 3]); // 0
Это позволяет избегать дополнительных проходов по массиву.
Функция scan возвращает индекс элемента, который
является минимальным по заданному компаратору. В отличие от
sort, массив не изменяется.
import { scan, ascending } from "d3-array";
const data = [5, 2, 8, 1];
scan(data, ascending); // 3 (индекс значения 1)
Особенность scan заключается в линейной сложности и
отсутствии побочных эффектов. Это делает её предпочтительной для больших
наборов данных, где сортировка избыточна.
Часто требуется многоуровневая сортировка: сначала по одному полю, затем по другому.
data.sort((a, b) =>
ascending(a.category, b.category) ||
descending(a.value, b.value)
);
Такой приём позволяет формировать предсказуемые и стабильные порядки, важные для таблиц и упорядоченных диаграмм.
Фильтрация в d3-экосистеме не выделена в отдельный уникальный API,
поскольку базовый инструмент уже существует в JavaScript —
Array.prototype.filter. Однако d3-array активно
используется вместе с фильтрацией через accessor-подход.
const filtered = data.filter(d => d.value > 10);
Часто фильтрация комбинируется с вычислением экстремумов:
const top = data
.filter(d => d.active)
.sort((a, b) => descending(a.value, b.value))
.slice(0, 5);
Здесь фильтрация определяет подмножество данных, а сортировка формирует порядок отображения.
В типичных задачах визуализации фильтрация используется вместе с функциями d3-array для вычислений:
import { max } from "d3-array";
const activeData = data.filter(d => d.active);
const maxValue = max(activeData, d => d.value);
Такой подход позволяет отделять семантику отбора данных от анализа их распределения.
При работе с объектами важно учитывать, что сортировка может изменять исходный массив. Поэтому часто создаётся копия:
const sorted = [...data].sort((a, b) => ascending(a.value, b.value));
Это критично в контексте реактивных интерфейсов, где исходные данные считаются неизменяемыми.
Компараторы позволяют строить более сложные функции отбора:
import { least } from "d3-array";
const cheapest = least(products, (a, b) => a.price - b.price);
Такой подход заменяет комбинации фильтрации и сортировки одной операцией, уменьшая сложность кода.
При вложенных данных accessor-функции становятся ключевым инструментом:
const maxRevenue = max(companies, d => d.metrics.revenue);
или комбинированный доступ:
const best = greatest(data, (a, b) =>
a.stats.score - b.stats.score
);
Это делает d3-array удобным для аналитических структур, где данные имеют несколько уровней вложенности.
Функции d3-array оптимизированы под линейные проходы:
max, min, scan,
least, greatest — O(n)sort — O(n log n)filter — O(n)При работе с большими наборами данных предпочтение отдаётся линейным операциям вместо полной сортировки, особенно когда требуется только экстремум или индекс.
Наиболее типичный стиль работы с d3-array — цепочки преобразований:
const result = data
.filter(d => d.region === "EU")
.sort((a, b) => descending(a.sales, b.sales))
.slice(0, 10);
или с предварительным анализом:
const threshold = max(data, d => d.value) * 0.8;
const filtered = data.filter(d => d.value >= threshold);
Такой подход позволяет строить выразительные пайплайны обработки данных без усложнения структуры кода.