Фильтрация выборок: .filter()

Метод .filter() используется для отбора элементов из текущей выборки D3. Он позволяет оставить только те DOM-узлы, которые удовлетворяют заданному условию. После фильтрации возвращается новая выборка, содержащая только подходящие элементы.

Синтаксис:

selection.filter(filterFunction)

или

selection.filter(selector)

Метод поддерживает два варианта фильтрации:

  • через callback-функцию;
  • через CSS-селектор.

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

Каждый элемент выборки проходит проверку. Если условие возвращает true, элемент остаётся в новой выборке. Если false — исключается.

Пример:

d3.selectAll("circle")
    .filter(function() {
        return true;
    });

В этом случае сохраняются все элементы.


Фильтрация через callback-функцию

Наиболее гибкий вариант работы.

Сигнатура callback-функции

function(d, i, nodes)

Параметры:

Параметр Описание
d Данные, привязанные к элементу
i Индекс элемента
nodes Массив DOM-элементов выборки

Фильтрация по данным

Пример: отображение только больших значений

const data = [10, 25, 40, 5, 60];

d3.select("body")
    .selectAll("div")
    .data(data)
    .enter()
    .append("div")
    .text(d => d)
    .filter(d => d > 20)
    .style("color", "red");

Что происходит

  1. Создаются элементы div.
  2. Каждому элементу привязываются данные.
  3. .filter(d => d > 20) оставляет только элементы со значением больше 20.
  4. Только отфильтрованные элементы получают красный цвет.

Фильтрация по индексу

Метод часто применяется для работы с позициями элементов.

Пример: выделение чётных элементов

d3.selectAll("li")
    .filter((d, i) => i % 2 === 0)
    .style("background", "#eee");

Результат

Стили применяются только к элементам с чётным индексом.


Использование this внутри .filter()

Внутри обычной функции this указывает на текущий DOM-элемент.

Пример

d3.selectAll("rect")
    .filter(function() {
        return this.getAttribute("width") > 100;
    })
    .attr("fill", "orange");

Здесь фильтрация выполняется по атрибуту DOM-элемента.


Почему стрелочные функции иногда не подходят

Стрелочные функции не имеют собственного this.

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

.filter(() => {
    return this.classList.contains("active");
})

Правильно:

.filter(function() {
    return this.classList.contains("active");
})

Фильтрация через CSS-селектор

.filter() может принимать строку-селектор.

Пример

d3.selectAll("div")
    .filter(".active")
    .style("color", "blue");

Будут выбраны только элементы с классом active.


Сравнение callback и CSS-селектора

Подход Возможности
CSS-селектор Простая фильтрация по классам, тегам, атрибутам
Callback-функция Полный контроль над логикой

Фильтрация SVG-элементов

Метод особенно полезен при построении визуализаций.

Пример: выделение столбцов диаграммы

const data = [15, 42, 8, 33, 27];

const svg = d3.select("body")
    .append("svg")
    .attr("width", 400)
    .attr("height", 200);

svg.selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", (d, i) => i * 60)
    .attr("y", d => 200 - d * 4)
    .attr("width", 50)
    .attr("height", d => d * 4)
    .attr("fill", "steelblue")
    .filter(d => d > 30)
    .attr("fill", "crimson");

Логика

Все прямоугольники создаются с синим цветом, затем фильтр выбирает значения больше 30 и перекрашивает их в красный.


Комбинирование .filter() с другими методами

Совместно с .attr()

d3.selectAll("circle")
    .filter(d => d.active)
    .attr("r", 10);

Совместно с .style()

d3.selectAll("text")
    .filter((d, i) => i < 3)
    .style("font-weight", "bold");

Совместно с .remove()

d3.selectAll("p")
    .filter(function() {
        return this.textContent === "";
    })
    .remove();

Удаляются пустые элементы.


Совместно с .classed()

d3.selectAll("path")
    .filter(d => d.selected)
    .classed("highlight", true);

Отличие .filter() от .select()

.select()

Выбирает один вложенный элемент.

selection.select("span")

.filter()

Отбирает элементы из уже существующей выборки.

selection.filter(".active")

Отличие .filter() от .selectAll()

.selectAll()

Создаёт новую выборку по селектору.

d3.selectAll(".item")

.filter()

Работает только с текущей выборкой.

selection.filter(".item")

Использование сложных условий

Пример

d3.selectAll("circle")
    .filter(function(d, i) {
        return d.value > 50 && i < 5;
    })
    .attr("fill", "green");

Цепочки фильтрации

Фильтрацию можно выполнять последовательно.

Пример

d3.selectAll("rect")
    .filter(d => d.visible)
    .filter(d => d.value > 100)
    .attr("stroke", "black");

Преимущества

  • читаемость;
  • разделение логики;
  • удобство отладки.

Работа с вложенными выборками

Пример

d3.selectAll("g")
    .filter(function() {
        return d3.select(this).selectAll("circle").size() > 3;
    })
    .attr("opacity", 0.5);

Фильтрация после data join

Метод часто применяется после связывания данных.

Пример

const items = svg.selectAll("circle")
    .data(data)
    .enter()
    .append("circle");

items
    .filter(d => d > 50)
    .attr("fill", "red");

Использование .filter() при анимациях

Пример

d3.selectAll("rect")
    .filter(d => d.changed)
    .transition()
    .duration(1000)
    .attr("height", d => d.value);

Анимация применяется только к изменённым элементам.


Производительность

.filter() перебирает все элементы выборки. При работе с большими объёмами данных важно учитывать стоимость вычислений.

Рекомендации

Избегать тяжёлых вычислений

Плохо:

.filter(d => complexCalculation(d))

Лучше заранее вычислить значения:

data.forEach(d => {
    d.valid = complexCalculation(d);
});

selection.filter(d => d.valid);

Минимизировать обращения к DOM

Плохо:

.filter(function() {
    return this.offsetWidth > 100;
})

DOM-операции относительно дорогие.


Порядок элементов сохраняется

.filter() не изменяет порядок элементов в выборке.

Пример

d3.selectAll("div")
    .filter((d, i) => i < 2);

Первые два элемента сохраняют исходный порядок.


Возврат новой выборки

Исходная выборка не изменяется.

const all = d3.selectAll("circle");

const large = all.filter(d => d > 100);
  • all содержит все элементы;
  • large — только отфильтрованные.

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

Ошибка: использование стрелочной функции с this

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

.filter(() => this.checked)

Правильно:

.filter(function() {
    return this.checked;
})

Ошибка: отсутствие возвращаемого значения

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

.filter((d) => {
    d > 10;
})

Правильно:

.filter((d) => {
    return d > 10;
})

или

.filter(d => d > 10)

Ошибка: путаница между .filter() массива и D3

D3-фильтрация работает с выборками DOM-элементов, а не с обычными массивами.

Массив:

array.filter(...)

D3:

selection.filter(...)

Практический пример: интерактивная таблица

const data = [
    {name: "A", value: 10},
    {name: "B", value: 50},
    {name: "C", value: 80},
    {name: "D", value: 20}
];

const rows = d3.select("body")
    .append("table")
    .selectAll("tr")
    .data(data)
    .enter()
    .append("tr");

rows.append("td")
    .text(d => d.name);

rows.append("td")
    .text(d => d.value);

rows
    .filter(d => d.value > 40)
    .style("background", "#ffdcdc");

Практический пример: фильтрация узлов графа

simulationNodes
    .filter(d => d.type === "server")
    .attr("r", 12)
    .attr("fill", "orange");

Практический пример: скрытие элементов

d3.selectAll(".item")
    .filter(d => !d.visible)
    .style("display", "none");

Внутреннее поведение метода

.filter() создаёт новую группу элементов, содержащую ссылки только на подходящие DOM-узлы.

Концептуально работа похожа на:

const filtered = nodes.filter(condition);

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


Когда использовать .filter()

Метод особенно полезен в следующих ситуациях:

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

Когда лучше использовать CSS

Если требуется только визуальное оформление по классам, предпочтительнее CSS.

Вместо:

selection.filter(".active")
    .style("color", "red");

Иногда лучше:

.active {
    color: red;
}

D3-фильтрация особенно ценна при динамических условиях, зависящих от данных.


Комбинация .filter() и событий

Пример

d3.selectAll("circle")
    .on("click", function(event, d) {

        d3.selectAll("circle")
            .filter(node => node.group === d.group)
            .attr("fill", "red");

    });

После клика выделяются элементы той же группы.


Итеративная обработка после фильтрации

d3.selectAll("rect")
    .filter(d => d.active)
    .each(function(d) {
        console.log(d);
    });

.each() будет вызван только для отфильтрованных элементов.


Краткая схема работы

selection
    .filter(условие)
    .дальнейшие_операции()

Этапы:

  1. Создание выборки.
  2. Проверка каждого элемента.
  3. Формирование новой выборки.
  4. Применение последующих операций только к отфильтрованным элементам.