Сортировка элементов: .sort() и .order()

В D3.js порядок DOM-элементов напрямую влияет на визуальное отображение графики. SVG-элементы, находящиеся ниже в DOM-дереве, рисуются поверх предыдущих. Поэтому сортировка элементов становится важной частью построения интерактивных визуализаций.

Методы .sort() и .order() позволяют:

  • изменять порядок элементов в DOM;
  • управлять слоями SVG;
  • переупорядочивать данные и связанные элементы;
  • корректно отображать анимации и переходы;
  • обновлять визуализацию после изменения данных.

Несмотря на внешнюю схожесть, методы решают разные задачи:

Метод Назначение
.sort() Сортирует элементы по данным
.order() Приводит DOM-порядок к порядку selection

Метод .sort()

Общий синтаксис

selection.sort(compare);

Параметр compare

Функция сравнения работает аналогично Array.prototype.sort():

(a, b) => число

Возвращаемое значение:

Результат Поведение
< 0 a раньше b
> 0 b раньше a
0 порядок не меняется

Принцип работы .sort()

Метод:

  1. берёт данные, привязанные к элементам;
  2. сортирует selection;
  3. физически меняет порядок DOM-узлов.

Это важно: .sort() изменяет не только внутренний порядок selection, но и структуру DOM.


Базовая сортировка

Исходные данные

const data = [8, 3, 15, 6, 1];

Создание элементов

d3.select("body")
    .selectAll("div")
    .data(data)
    .enter()
    .append("div")
    .text(d => d);

Результат:

<div>8</div>
<div>3</div>
<div>15</div>
<div>6</div>
<div>1</div>

Сортировка по возрастанию

d3.selectAll("div")
    .sort((a, b) => a - b);

Результат:

<div>1</div>
<div>3</div>
<div>6</div>
<div>8</div>
<div>15</div>

Сортировка по убыванию

d3.selectAll("div")
    .sort((a, b) => b - a);

Сортировка объектов

Массив объектов

const users = [
    { name: "Анна", age: 28 },
    { name: "Игорь", age: 19 },
    { name: "Мария", age: 34 }
];

Создание элементов

d3.select("body")
    .selectAll("p")
    .data(users)
    .enter()
    .append("p")
    .text(d => `${d.name}: ${d.age}`);

Сортировка по возрасту

d3.selectAll("p")
    .sort((a, b) => a.age - b.age);

Сортировка строк

По алфавиту

.sort((a, b) => d3.ascending(a.name, b.name))

В обратном порядке

.sort((a, b) => d3.descending(a.name, b.name))

Функции d3.ascending() и d3.descending()

D3 предоставляет готовые компараторы.

d3.ascending(a, b)

Возвращает:

  • -1, если a < b
  • 1, если a > b
  • 0, если равны

Пример:

.sort((a, b) => d3.ascending(a.value, b.value))

d3.descending(a, b)

Обратная сортировка:

.sort((a, b) => d3.descending(a.value, b.value))

Сортировка SVG-элементов

Это один из наиболее важных сценариев использования.

Пример с окружностями

const data = [
    { r: 10 },
    { r: 40 },
    { r: 25 }
];

const svg = d3.select("svg");

svg.selectAll("circle")
    .data(data)
    .enter()
    .append("circle")
    .attr("cx", 100)
    .attr("cy", 100)
    .attr("r", d => d.r);

Проблема наложения

SVG рисует элементы сверху вниз по DOM.

Если большой круг находится раньше маленького, маленький может оказаться скрытым.


Решение через .sort()

svg.selectAll("circle")
    .sort((a, b) => b.r - a.r);

Теперь большие окружности окажутся под маленькими.


Сортировка столбцов диаграммы

Создание bar chart

const data = [12, 5, 18, 7, 22];

const svg = d3.select("svg");

svg.selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", (d, i) => i * 40)
    .attr("y", d => 200 - d * 5)
    .attr("width", 30)
    .attr("height", d => d * 5);

Сортировка столбцов

svg.selectAll("rect")
    .sort((a, b) => b - a);

Однако визуально позиции не изменятся автоматически, потому что координаты x уже заданы.

.sort() изменяет DOM-порядок, но не вычисленные координаты.


Комбинация с transition

Обычно сортировка сопровождается анимацией.

Пример

svg.selectAll("rect")
    .sort((a, b) => b - a)
    .transition()
    .duration(1000)
    .attr("x", (d, i) => i * 40);

Теперь столбцы плавно перестроятся.


Как работает .sort() внутри

Упрощённо механизм выглядит так:

  1. D3 копирует элементы selection;
  2. выполняет сортировку;
  3. переставляет DOM-узлы через insertBefore.

Следовательно:

  • операция может быть дорогой на больших объёмах;
  • частые сортировки ухудшают производительность;
  • сложные SVG лучше сортировать минимально.

Особенности стабильности сортировки

JavaScript-сортировка в современных движках стабильна, но исторически это было не всегда.

Если элементы равны:

.sort((a, b) => a.value - b.value)

то одинаковые значения обычно сохраняют исходный порядок.


Сортировка при обновлении данных

Один из ключевых сценариев D3 — обновление визуализации.

Пример

function update(data) {

    const bars = svg.selectAll("rect")
        .data(data);

    bars.enter()
        .append("rect")
        .merge(bars)
        .sort((a, b) => b - a)
        .transition()
        .attr("x", (d, i) => i * 40);
}

Метод .order()

Назначение

.order() переставляет DOM-элементы в соответствии с текущим порядком selection.

Синтаксис:

selection.order();

Метод не сортирует данные самостоятельно.


Когда нужен .order()

Основной сценарий:

  • selection уже имеет нужный порядок;
  • DOM ещё не синхронизирован.

Отличие .sort() от .order()

.sort()

  • сортирует данные;
  • меняет порядок selection;
  • меняет DOM.

.order()

  • не сортирует;
  • не изменяет данные;
  • только обновляет DOM-порядок.

Пример работы .order()

Исходные данные

const data = [
    { id: 1, value: 20 },
    { id: 2, value: 5 },
    { id: 3, value: 12 }
];

Создание selection

const items = d3.select("body")
    .selectAll("div")
    .data(data)
    .enter()
    .append("div")
    .text(d => d.value);

Изменение порядка selection

const sorted = items.data()
    .sort((a, b) => a.value - b.value);

DOM пока не изменился.


Применение .order()

items.order();

Теперь DOM будет соответствовать текущему порядку selection.


Использование после .raise() и .lower()

D3 содержит методы управления слоями:

Метод Действие
.raise() Поднимает элемент наверх
.lower() Опускает вниз

После множества таких операций DOM может стать неупорядоченным.

.order() помогает восстановить корректную структуру.


Связь с enter/update/exit

При data join порядок элементов особенно важен.

Пример

const update = svg.selectAll("circle")
    .data(data, d => d.id);

После обновления:

update.order();

Это гарантирует правильный порядок элементов.


Практика: сортировка пузырьковой диаграммы

Данные

const data = [
    { r: 20 },
    { r: 50 },
    { r: 10 },
    { r: 35 }
];

Создание кругов

svg.selectAll("circle")
    .data(data)
    .enter()
    .append("circle")
    .attr("cx", 150)
    .attr("cy", 150)
    .attr("r", d => d.r);

Правильный порядок слоёв

svg.selectAll("circle")
    .sort((a, b) => b.r - a.r);

Большие круги будут сзади, маленькие — спереди.


Интерактивная сортировка

Кнопка сортировки

d3.select("button")
    .on("click", () => {

        svg.selectAll("rect")
            .sort((a, b) => b - a)
            .transition()
            .duration(800)
            .attr("x", (d, i) => i * 40);

    });

Сортировка по нескольким полям

Пример

.sort((a, b) => {

    if (a.score !== b.score) {
        return b.score - a.score;
    }

    return d3.ascending(a.name, b.name);
})

Оптимизация производительности

Минимизация DOM-операций

DOM-перестановки — дорогая операция.

Лучше:

  • сортировать только необходимые элементы;
  • избегать повторных .sort() внутри анимаций;
  • обновлять порядок пакетно.

Предварительная сортировка данных

Иногда эффективнее сортировать массив заранее:

data.sort((a, b) => b.value - a.value);

А затем выполнять обычный data join.


Частые ошибки

Ожидание изменения координат

Ошибка:

.sort((a, b) => b - a);

Позиции элементов не меняются автоматически.

Нужно отдельно обновлять:

.attr("x", ...)
.attr("y", ...)

Путаница между .sort() и .order()

.sort():

selection.sort(...)

.order():

selection.order()

Первый принимает компаратор, второй — нет.


Сортировка без привязанных данных

.sort() использует данные элементов.

Если данных нет:

d3.selectAll("div").sort();

результат будет бессмысленным либо приведёт к ошибкам.


Влияние на transitions

При использовании transition порядок операций важен.

Правильно:

selection
    .sort(...)
    .transition();

Неправильно:

selection
    .transition()
    .sort(...);

Сортировка и z-index в SVG

SVG не поддерживает CSS z-index так, как HTML.

Порядок элементов определяется исключительно DOM-позицией.

Именно поэтому .sort(), .order(), .raise() и .lower() играют критическую роль в SVG-визуализациях.


Комбинация .filter() и .sort()

Пример

svg.selectAll("circle")
    .filter(d => d.active)
    .sort((a, b) => b.value - a.value);

Сортируются только активные элементы.


Использование с force layout

В force-directed graph порядок влияет на читаемость:

nodes.sort((a, b) => b.radius - a.radius);

Мелкие узлы оказываются поверх крупных.


Использование в choropleth-картах

При отображении географических областей:

paths.sort((a, b) => b.population - a.population);

мелкие регионы могут отображаться поверх крупных.


Сортировка в canvas-визуализациях

При работе через <canvas> методы .sort() и .order() напрямую не управляют отрисовкой.

Но сортировка данных перед рендером остаётся важной:

data.sort((a, b) => a.depth - b.depth);

Внутренний порядок selection

Selection в D3 — это массив групп элементов.

.sort() изменяет:

  • порядок внутри групп;
  • DOM-структуру;
  • последовательность обхода.

.order() работает только с DOM-позицией.


Поведение при одинаковых элементах

.sort((a, b) => 0)

DOM обычно остаётся неизменным.

Но D3 всё равно может выполнять проверку порядка.


Практический шаблон сортировки диаграммы

function sortBars() {

    svg.selectAll("rect")
        .sort((a, b) => b.value - a.value)
        .transition()
        .duration(1000)
        .attr("x", (d, i) => i * 50);
}

Практический шаблон управления слоями

svg.selectAll("circle")
    .sort((a, b) => b.r - a.r);

Практический шаблон восстановления порядка

selection.order();

Ключевые различия

Особенность .sort() .order()
Сортирует данные Да Нет
Требует compare-функцию Да Нет
Меняет DOM Да Да
Меняет порядок selection Да Нет
Используется для слоёв SVG Да Да
Подходит для анимированной перестройки Да Нет