Компактные массивы: compact

Валидация массивов в Yup строится вокруг декларативного описания структуры данных и последовательного применения трансформаций и ограничений. Одним из ключевых механизмов предварительной очистки данных внутри схемы массива является метод compact, позволяющий исключать нежелательные элементы до выполнения основной валидации.

При работе с пользовательскими данными массивы часто содержат «шум»: null, undefined, пустые строки, некорректные значения, оставшиеся после работы форм или API. Если такие элементы не обработать заранее, они могут привести к ошибкам валидации или искажению результатов проверок min, max, of и других ограничений.

В Yup массив описывается через:

import * as Yup from 'yup';

const schema = Yup.array().of(
  Yup.string()
);

Однако такой схемы недостаточно, если входные данные нестабильны и требуют предварительной фильтрации.

Назначение compact

Метод compact предназначен для удаления элементов массива по заданному правилу перед тем, как к нему применяются остальные валидаторы.

Сигнатура:

array().compact(rejector?: (value, index, array) => boolean)

Поведение зависит от переданного аргумента:

  • если rejector не указан — удаляются «пустые» значения (аналогично поведению lodash.compact);
  • если rejector указан — удаляются элементы, для которых функция возвращает true.

Базовое использование

Наиболее простой сценарий — очистка массива от falsy-значений:

const schema = Yup.array()
  .compact()
  .of(Yup.string());

В этом случае из массива будут исключены значения, которые интерпретируются как ложные: undefined, null, '', false, 0, NaN.

После очистки оставшиеся элементы проходят стандартную проверку of(Yup.string()).

Использование с пользовательским фильтром

Более гибкий вариант — передача функции-предиката:

const schema = Yup.array()
  .compact((value) => value === null || value === undefined)
  .of(Yup.number());

Здесь удаляются только null и undefined, а остальные значения остаются без изменений.

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

(value, index, array) => boolean

Это позволяет строить более сложные условия:

const schema = Yup.array()
  .compact((value, index) => index % 2 === 0)
  .of(Yup.string());

В данном случае удаляются элементы с чётными индексами.

Влияние compact на последующую валидацию

Важно понимать, что compact изменяет сам массив до применения остальных правил схемы.

Например:

const schema = Yup.array()
  .compact()
  .min(3);

Если входной массив:

[1, null, 2, undefined, 3]

После compact:

[1, 2, 3]

Именно этот результат участвует в проверке min(3).

Отличие от transform

В Yup существует метод transform, который также позволяет изменять входные данные, но его семантика отличается.

compact:

  • специализирован для массивов;
  • удаляет элементы;
  • возвращает новый массив без «плохих» значений;
  • работает как фильтрация.

transform:

  • универсален для любых типов;
  • изменяет значение произвольным образом;
  • требует ручного возврата массива.

Пример эквивалента через transform:

array().transform((value) =>
  value.filter((v) => v != null)
)

Однако compact является более декларативным и читаемым решением именно для задачи очистки массива.

Порядок выполнения в цепочке

При построении схемы порядок методов имеет значение:

Yup.array()
  .compact()
  .of(Yup.string().required())
  .min(1)

Логика выполнения:

  1. применяется compact;
  2. формируется массив значений;
  3. запускается of(...);
  4. применяются ограничения массива (min, max, length и т.д.).

Если переставить вызовы, результат может измениться, особенно при строгих ограничениях внутри of.

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

compact не ограничивается примитивами. При работе с объектами фильтрация строится по ссылке на элемент:

const schema = Yup.array()
  .compact((obj) => obj?.active === false)
  .of(
    Yup.object({
      id: Yup.number().required(),
      active: Yup.boolean()
    })
  );

Здесь исключаются все неактивные элементы до запуска валидации объектов.

Поведение с пустыми массивами

Если после применения compact массив становится пустым, дальнейшее поведение зависит от остальных правил:

Yup.array()
  .compact()
  .min(1, 'Минимум один элемент');

Вход:

[null, undefined]

После compact:

[]

Результат — ошибка min.

Типичные сценарии применения

Фильтрация форм с динамическими полями:

const schema = Yup.array()
  .compact()
  .of(Yup.string().trim().min(1));

Очистка API-данных:

const schema = Yup.array()
  .compact((v) => typeof v !== 'number')
  .of(Yup.number());

Обработка списков с необязательными значениями:

const schema = Yup.array()
  .compact()
  .of(Yup.mixed());

Поведение при мутациях данных

compact не изменяет исходный массив напрямую в пользовательском коде, но создаёт промежуточное преобразование внутри схемы. Это означает, что оригинальные данные сохраняются вне контекста Yup, однако внутренняя валидация работает уже с очищенной версией массива.

Комбинация с nested-схемами

При использовании сложных структур compact часто применяется до глубоких проверок:

const schema = Yup.array()
  .compact()
  .of(
    Yup.object({
      name: Yup.string().required(),
      age: Yup.number().min(0)
    })
  )
  .required();

Это снижает нагрузку на валидацию вложенных схем за счёт предварительного удаления некорректных элементов.

Ограничения и особенности

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