Как отсортировать диапазон в Google Sheets с помощью Apps Script?

Google Sheets предоставляет мощные встроенные инструменты для сортировки данных. Однако, когда требуется сложная логика, автоматизация или интеграция с другими сервисами, Google Apps Script становится незаменимым решением. Он позволяет программно управлять процессом сортировки, открывая широкие возможности для кастомизации.

Зачем использовать Apps Script для сортировки?

Автоматизация: Запуск сортировки по расписанию, при изменении данных (onEdit), открытии таблицы (onOpen) или по нажатию кнопки.

Сложная логика: Реализация нестандартных критериев сортировки, которые невозможно задать через интерфейс.

Интеграция: Включение сортировки в более крупные рабочие процессы, взаимодействующие с другими Google Workspace сервисами или внешними API.

Динамические диапазоны: Сортировка данных, размер и положение которых могут изменяться.

Основные понятия: диапазоны, листы и объекты Apps Script

Прежде чем перейти к сортировке, важно понимать ключевые объекты Apps Script:

SpreadsheetApp: Корневой сервис для работы с Google Sheets.

Spreadsheet: Представляет конкретную Google Таблицу.

Sheet: Представляет отдельный лист в таблице.

Range: Представляет прямоугольную область ячеек на листе. Именно с объектами Range мы будем работать при сортировке.

Получение доступа к диапазону обычно выглядит так:

/**
 * Получает активный лист и указанный диапазон.
 * @returns {GoogleAppsScript.Spreadsheet.Range | null} Объект диапазона или null, если лист не найден.
 */
function getTargetRange(): GoogleAppsScript.Spreadsheet.Range | null {
  const ss: GoogleAppsScript.Spreadsheet.Spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const sheet: GoogleAppsScript.Spreadsheet.Sheet | null = ss.getSheetByName('Отчет по кампаниям'); // Замените на имя вашего листа
  if (!sheet) {
    Logger.log('Лист "Отчет по кампаниям" не найден.');
    return null;
  }
  // Пример: диапазон A2:E, исключая заголовок в строке 1
  const range: GoogleAppsScript.Spreadsheet.Range = sheet.getRange('A2:E' + sheet.getLastRow()); 
  return range;
}

Методы сортировки диапазонов в Google Sheets Apps Script

Apps Script предоставляет гибкий метод sort() для объекта Range.

Использование метода `sort()` для базовой сортировки

Простейший вызов range.sort(columnPosition) сортирует диапазон по указанному столбцу (индекс начинается с 1) в порядке возрастания.

/**
 * Сортирует диапазон A2:E на листе 'Отчет по кампаниям' по первому столбцу (A) по возрастанию.
 */
function simpleSortByFirstColumn(): void {
  const range: GoogleAppsScript.Spreadsheet.Range | null = getTargetRange();
  if (range) {
    range.sort(1); // Сортировка по первому столбцу (индекс 1)
    Logger.log('Диапазон отсортирован по столбцу A.');
  }
}

Сортировка по нескольким столбцам с помощью `sort(sortSpecObj)`

Для более сложной сортировки используется перегруженная версия метода sort(), принимающая объект спецификации сортировки (sortSpecObj). Это позволяет задать несколько столбцов для сортировки и указать направление (возрастание/убывание) для каждого.

/**
 * Сортирует данные по рекламным кампаниям: сначала по CTR (столбец D) по убыванию,
 * затем по Названию кампании (столбец A) по возрастанию.
 */
function multiColumnSortExample(): void {
  const range: GoogleAppsScript.Spreadsheet.Range | null = getTargetRange(); 
  if (range) {
    // CTR - 4-й столбец в диапазоне A2:E
    // Название кампании - 1-й столбец в диапазоне A2:E
    range.sort([
      { column: 4, ascending: false }, // Сортировка по CTR (убывание)
      { column: 1, ascending: true }   // Затем по Названию (возрастание)
    ]);
    Logger.log('Диапазон отсортирован по CTR (убывание), затем по Названию (возрастание).');
  }
}

Настройка параметров сортировки: `sortSpecObj`

Объект sortSpecObj может быть одним объектом или массивом объектов, каждый из которых описывает правило сортировки для одного столбца:

column: Number. Индекс столбца для сортировки (относительно начала диапазона, не листа!). Индекс начинается с 1.

ascending: Boolean. true для сортировки по возрастанию, false для сортировки по убыванию.

При передаче массива объектов сортировка происходит последовательно: сначала по первому объекту в массиве, затем строки с одинаковыми значениями в первом столбце сортируются по второму объекту и т.д.

Практические примеры сортировки с использованием Apps Script

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

/**
 * Сортирует данные о кликах (столбец C) по убыванию.
 * @param {GoogleAppsScript.Spreadsheet.Range} rangeToSort Диапазон для сортировки.
 */
function sortByClicksDescending(rangeToSort: GoogleAppsScript.Spreadsheet.Range): void {
   // Столбец C - 3-й в диапазоне A2:E
  rangeToSort.sort({ column: 3, ascending: false });
  Logger.log('Диапазон отсортирован по кликам (убывание).');
}

/**
 * Сортирует данные по названию кампании (столбец A) по возрастанию.
 * @param {GoogleAppsScript.Spreadsheet.Range} rangeToSort Диапазон для сортировки.
 */
function sortByCampaignNameAscending(rangeToSort: GoogleAppsScript.Spreadsheet.Range): void {
  // Столбец A - 1-й в диапазоне A2:E
  rangeToSort.sort({ column: 1, ascending: true });
  Logger.log('Диапазон отсортирован по названию кампании (возрастание).');
}

// Пример использования:
function runSortExamples(): void {
  const range = getTargetRange();
  if(range) {
    // Сначала по убыванию кликов
    sortByClicksDescending(range);
    SpreadsheetApp.flush(); // Применить изменения перед следующей сортировкой
    Utilities.sleep(1000); // Небольшая пауза (опционально)

    // Затем по названию кампании
    // sortByCampaignNameAscending(range); // Раскомментируйте, если нужно применить обе
  }
}

Сортировка по дате или числу

Метод sort() автоматически определяет тип данных (число, дата, текст) и применяет соответствующее сравнение. Убедитесь, что данные в столбце имеют однородный формат. Проблемы могут возникнуть, если в числовом столбце есть текст или даты хранятся как строки.

/**
 * Сортирует диапазон по столбцу с датами (например, столбец B) по возрастанию.
 * @param {GoogleAppsScript.Spreadsheet.Range} rangeToSort Диапазон для сортировки.
 */
function sortByDateAscending(rangeToSort: GoogleAppsScript.Spreadsheet.Range): void {
  // Предполагаем, что даты находятся во 2-м столбце диапазона
  rangeToSort.sort({ column: 2, ascending: true });
  Logger.log('Диапазон отсортирован по дате (возрастание).');
}
Реклама

Сортировка с учетом регистра (или без)

Стандартная сортировка в Google Sheets не учитывает регистр символов (‘Apple’ и ‘apple’ считаются одинаковыми для целей сортировки). Чтобы реализовать сортировку с учетом регистра, необходимо извлечь данные, отсортировать их в JavaScript (который по умолчанию учитывает регистр при сравнении строк), а затем записать отсортированные данные обратно.

/**
 * Сортирует диапазон по первому столбцу с учетом регистра.
 * ВНИМАНИЕ: Перезаписывает данные в диапазоне.
 */
function caseSensitiveSort(): void {
  const range: GoogleAppsScript.Spreadsheet.Range | null = getTargetRange();
  if (!range) return;

  // 1. Получаем данные из диапазона
  const data: any[][] = range.getValues();

  // 2. Сортируем массив данных с использованием стандартного Array.prototype.sort()
  //    Сравнение строк в JavaScript учитывает регистр.
  data.sort((a, b) => {
    const valueA: string = String(a[0]); // Сравниваем по первому столбцу (индекс 0)
    const valueB: string = String(b[0]);
    if (valueA  valueB) {
      return 1;
    }
    return 0;
  });

  // 3. Записываем отсортированные данные обратно в диапазон
  range.setValues(data);
  Logger.log('Диапазон отсортирован по первому столбцу с учетом регистра.');
}

Для сортировки без учета регистра (если стандартное поведение range.sort() не подходит по каким-то причинам), можно привести строки к одному регистру перед сравнением в функции sort: data.sort((a, b) => String(a[0]).toLowerCase().localeCompare(String(b[0]).toLowerCase()));

Автоматизация сортировки: триггеры и пользовательские функции

Использование триггеров для автоматической сортировки при изменении данных

Триггер onEdit(e) позволяет запускать функцию сортировки каждый раз, когда пользователь редактирует ячейку. Важно оптимизировать такие триггеры, чтобы они срабатывали только при изменении нужных данных и не замедляли работу пользователя.

/**
 * Простой триггер onEdit для автоматической сортировки данных
 * при редактировании любого значения в столбцах A-E листа 'Отчет по кампаниям'.
 * Сортирует по столбцу D (CTR) по убыванию.
 *
 * @param {GoogleAppsScript.Events.SheetsOnEdit} e Объект события редактирования.
 */
function onEditAutoSort(e: GoogleAppsScript.Events.SheetsOnEdit): void {
  const editedRange: GoogleAppsScript.Spreadsheet.Range = e.range;
  const sheet: GoogleAppsScript.Spreadsheet.Sheet = editedRange.getSheet();
  const sheetName: string = 'Отчет по кампаниям'; // Целевой лист
  const sortColumnIndex: number = 4; // Индекс столбца для сортировки (D)

  // Проверяем, что редактирование произошло на нужном листе и в нужных столбцах
  if (sheet.getName() === sheetName && editedRange.getColumn() >= 1 && editedRange.getColumn()  headerRows) {
      const dataRange: GoogleAppsScript.Spreadsheet.Range = sheet.getRange(headerRows + 1, 1, sheet.getLastRow() - headerRows, 5); // A2:E...
      dataRange.sort({ column: sortColumnIndex, ascending: false });
      // Logger.log('Автоматическая сортировка выполнена.'); // Можно раскомментировать для отладки
    }
  }
}

Не забудьте установить триггер onEditAutoSort на событие onEdit через редактор скриптов (Триггеры -> Добавить триггер). Используйте e.source для получения объекта Spreadsheet вместо getActiveSpreadsheet() в триггерах.

Создание пользовательской функции для сортировки диапазона по требованию

Пользовательские функции позволяют запускать скрипты прямо из интерфейса Google Sheets (например, из меню или по нажатию на изображение).

/**
 * Выполняется при открытии таблицы, добавляет пользовательское меню.
 */
function onOpen(): void {
  SpreadsheetApp.getUi()
    .createMenu('Сортировка')
    .addItem('Сортировать отчет по CTR (убывание)', 'sortReportByCTRDesc')
    .addItem('Сортировать отчет по Названию (возрастание)', 'sortReportByNameAsc')
    .addToUi();
}

/**
 * Функция, вызываемая из меню для сортировки по CTR.
 */
function sortReportByCTRDesc(): void {
  const range = getTargetRange();
  if(range) {
     // Столбец D - 4-й в диапазоне A2:E
     sortByClicksDescending(range); // Используем ранее созданную функцию
     SpreadsheetApp.getActiveSpreadsheet().toast('Отчет отсортирован по CTR (убывание).');
  }
}

/**
 * Функция, вызываемая из меню для сортировки по Названию.
 */
function sortReportByNameAsc(): void {
  const range = getTargetRange();
  if(range) {
    // Столбец A - 1-й в диапазоне A2:E
    sortByCampaignNameAscending(range); // Используем ранее созданную функцию
    SpreadsheetApp.getActiveSpreadsheet().toast('Отчет отсортирован по Названию (возрастание).');
  }
}

Распространенные ошибки и их решения

Ошибки при определении диапазона

Неправильное имя листа: Убедитесь, что getSheetByName() использует точное имя листа.

Неверные границы диапазона: getRange('A1:B') может захватить тысячи пустых строк. Используйте getLastRow() и getLastColumn() для определения фактического размера данных: sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).

Сортировка заголовков: Исключайте строки заголовков из диапазона сортировки, если они не должны перемещаться.

Неправильная настройка `sortSpecObj`

Индекс столбца: Помните, что column в sortSpecObj отсчитывается от начала диапазона, а не от начала листа. Если диапазон C2:E10, то столбец C имеет индекс 1, D2, E3.

Тип данных: Массив спецификаций [{ column: 4, ascending: false }, { column: 1, ascending: true }] указывается в квадратных скобках, даже если сортировка идет по одному столбцу с расширенными параметрами.

Проблемы с правами доступа к скрипту и таблице

Авторизация: При первом запуске скрипт запросит разрешение на доступ к вашим Google Sheets. Убедитесь, что вы предоставили необходимые права.

Триггеры: Триггеры onEdit или onOpen могут иметь ограничения. Простые триггеры (onEdit(e)) выполняются от имени пользователя, редактирующего таблицу, и не могут выполнять действия, требующие авторизации, которой у этого пользователя нет. Устанавливаемые триггеры (через меню редактора скриптов) выполняются от имени создателя триггера, но требуют явной авторизации при установке.

Защищенные диапазоны: Скрипт может не сработать, если диапазон защищен от редактирования, а у пользователя (или скрипта, если триггер установлен) нет прав на его изменение.


Добавить комментарий