Задать вопрос
  • IMPORTXML пропускает строки при парсинге. Как решить?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Пример в Таблице https://docs.google.com/spreadsheets/d/1gpb-ZRLUW5...

    Задайте не точное совпадение класса, а содержание его в атрибуте

    div[contains (@class, 't-row-div')]

    =IMPORTXML(
      "https://www.triatron.ru/catalog/index.php?rodgr=27576&gr=9832,214848&filter%5B0%5D%5B%5D=TITAN&sort=0&dir=0&p=1&all=1";
      "//div[contains (@class, 't-row-div')]//div[1][@class='t-cell-div']"
    )


    Возможно, ваша выборка неожиданная, т.к. есть пробелы между ячейками. Это происходит из-за того, что вы выбираете массив, а не ветвь в дереве. Сравните второй //div и /div:
    "//div[contains (@class, 't-row-div')]//div[1][@class='t-cell-div']"

    и
    "//div[contains(@class, 't-row-div')]/div[1][@class='t-cell-div']"


    • // - выбрать все множества по дереву вниз
    • / - выбрать следующую ветвь


    62307381dfa08035413307.png
    Ответ написан
    4 комментария
  • Как объединить значения из нескольких ячеек в одной?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Надеюсь, вот это вам поможет

    =G7 & " - " & F7 & " - " & D7

    или, что более аутентично для таких расчетов

    =JOIN(" - ";G7;F7;D7) - в зависимости от локали тут могут быть запятые , вместо точек с запятыми ;.
    Ответ написан
    Комментировать
  • Как запретить удаление листов в гугл таблице?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вы можете просто защитить лист

    622b00cf027b5239384520.png

    Подобные автоматизации ни к чему - только мешают работать.
    Ответ написан
    Комментировать
  • Как изменить значение в зависимости от цвета ячейки?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Можно вот так https://docs.google.com/spreadsheets/d/1hl1zEArivM...

    /**
     *  @customfunction
     */
    function COLORED(range) {
      const book = SpreadsheetApp.getActiveSpreadsheet();
      const formula = book.getActiveRange().getFormula();
      const [skip, addr] = formula.match(/\((.*)\)/) || [undefined, undefined];
      if(addr){
        const range = (/!/.test(addr) ? book : book.getActiveSheet()).getRange(addr);
        return range.getBackgrounds().map(row => row.map(cell => {
          let res = ''
          switch(cell){
            case '#4285f4': res = 'Да';
            break;
            case '#ea4335': res = 'Нет';
            break;
            default:
            break;
          }
          return res;
        }));
      }
      return '-';
    }


    622a3093717d5512699131.png

    Но, к сожалению, это не реагирует на изменение цвета, только на значение.
    Ответ написан
    Комментировать
  • Как сопоставлять данные на двух листах Google Sheets для учета движения товаров?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Для такого скрипта достаточно взять данные из архива, перевести массив для удобства в плоский (индекс) и производить поиск по этому индексу. Остальное можно найти в сети.
    Ответ написан
    Комментировать
  • Google APPS — Как запустить нескольких скриптов?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вы должны перестроить код так, чтобы sheet указывал на нужный лист, например, вместо

    const sheet = SpreadsheetApp
        .getActiveSpreadsheet()
        .getSheetByName('Data')


    const id = 'ABSDADFASDFSDFASDFASD123123';
      const sheet = SpreadsheetApp
        .getSpreadsheetById(id)
        .getSheetByName('Data')


    Далее разбить функцию на части, которые могут вызывать этот код, например,

    const id = 'ABSDADFASDFSDFASDFASD123123';
      createNewGoogleDocs(id);
    Ответ написан
    Комментировать
  • Как сделать автоматическое открытие листа по дню Google Таблицы?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Пример в Таблице https://docs.google.com/spreadsheets/d/1afP_NGm4V7...

    Я бы перемещал лист в начало раз в день по триггеру - это намного аккуратнее и не мельтешит.

    function triggerSction() {
      const book = SpreadsheetApp.getActiveSpreadsheet();
      const sheetName = 
        Utilities.formatDate(new Date(), book.getSpreadsheetTimeZone(), 'dd.MM');
      const sheet = book.getSheetByName(sheetName);
      if (sheet) {
        sheet.activate();
        book.moveActiveSheet(1);
      }
    }
    Ответ написан
    Комментировать
  • С помощью какой функции можно узнать сколько объединено ячеек?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Пример в Таблице https://docs.google.com/spreadsheets/d/1mkUDHVX1b5...

    Попробуйте Range.isPartOfMerge()

    function myFunction() {
      const book = SpreadsheetApp.getActiveSpreadsheet();
      const range = book.getRange('C5');
      const mergedRange = range.getMergedRanges()[0];
      if (mergedRange) {
        const countCells = mergedRange.getValues()[0].length * mergedRange.getValues().length;
        console.log('Входит в объединенную ячейку?', range.isPartOfMerge());
        console.log('Диапазон объединения:', mergedRange.getA1Notation());
        console.log('Количество объединенных ячеек:', countCells);
        console.log('Количество строк в объединенном диапазоне:', mergedRange.getValues().length);
      } else {
        console.info('Ячейка не объединена');
      }
    }


    622a11222454e176897771.png
    Ответ написан
    4 комментария
  • Как проверить google script есть ли с таким же именем лист в таблице как и активный?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вероятнее всего, вы хотите проверить, есть ли лист с заданным именем в книге. Просто возьмите этот лист - сразу станет понятно - он есть или его нет.

    Пример

    const sheetName = 'Sheet1';
    const sheet = book.getSheetByName(sheetName);
    if(sheet){
      cnsole.log(`Лист с именем ${sheetName} есть`);
    }


    Или, что чаще используется, создание листа, если его нет

    const sheetName = 'Sheet1';
    const sheet = book.getSheetByName(sheetName) || book.insertSheet(sheetName);
    Ответ написан
    Комментировать
  • Как закрасить границу объединённой ячейки программно?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Пример https://docs.google.com/spreadsheets/d/1xa_zzf6P1n...

    62282dc7c20f7732836185.png

    function myFunction() {
      const book = SpreadsheetApp.getActiveSpreadsheet();
      const sheet = book.getSheetByName('Sheet1');
      const ranges = ['B1', 'B4', 'B6', 'B9'];
      sheet.getRangeList(ranges).setBorder(null, null, true, null, null, null);
    }


    Все работает как надо.
    Вы не привели нормальный пример, и в этом ваша основная ошибка. Скорее всего вы смотрите не на ту часть кода.
    Ответ написан
    Комментировать
  • IMPORTXML заголовка H1 в Гугл Таблицах почему не работает?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Скорее всего проблема на стороне Гугл https://support.google.com/docs/thread/153166088/ Обещают решить.
    Ответ написан
    Комментировать
  • Как объединить 2 разные таблицы в Google Spreadsheets?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Пример в Таблице https://docs.google.com/spreadsheets/d/1sBq2EzzxR0...

    Вам не нужен QUERY. Вам нужен VLOOKUP <== ссылка

    К сожалению, объединить сразу не получится, придется делать три формулы, но они простые. Вот главная из них

    =ARRAYFORMULA(IFERROR(
      VLOOKUP(A1:A17;Shifts!A:H;{2\3\4\5\6};0);
      VLOOKUP(A1:A17;KPI!A:L;{4\5\2\12\3};0)
    ))


    6226ebc2ec9fa960504504.png
    Ответ написан
    3 комментария
  • Как перенести значения на новые строки по условию?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Из простого и незатейливого можно попробовать вот это

    =ARRAYFORMULA(QUERY(
      SPLIT(FLATTEN(A1:A5& "|" & B1:B5 & "|" & C1:F5);"|");
      "where Col3<>''"
    ))


    62135b41b4037281550805.png
    Ответ написан
    1 комментарий
  • Как отобрать определённое количество ячеек с самой старой историей изменения?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Никак
    Ответ написан
    Комментировать
  • Как произвести удаление определенного типа файлов с Google Drive?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Чтобы было n-дней, передайте параметр вместо 7-ки тут

    var n = 7;
    var ThirtyDaysBeforeNow = new Date().getTime()-3600*1000*24*n;


    Чтобы указать тип, дополните в этом месте

    var files = DriveApp.searchFiles(
         'modifiedDate < "' + cutOffDateAsString + 
         '" and (mimeType = "application/zip" or mimeType = "image/jpg")');


    Думаю, что есть какие-то псевдо типы, которые могут совмещать image/jpg и image/jpeg, но не факт.

    Чтобы очищать корзину, вызовите после удаления вот это
    Drive.Files.emptyTrash();

    Вы должны подключить Advanced Service под названием Drive
    620f43c3dc7cb754862703.png
    Ответ написан
  • Как добавить связку данных таблиц в Confluence с данными на других страницах?

    oshliaer
    @oshliaer
    Google Products Expert
    blabs, однозначного ответа, скорее всего нет. Я бы предположил, что документация должна идти к коду при разработке и от кода - при поддержке. Т.е. в конечном итоге сначала у вас есть ТЗ на которое ссылается Confluence (оставаясь точкой отправления), а потом у вас есть код с должным набором комментариев и документов в самой кодовой базе, на которые Confluence ссылается как на результат работы, а код ссылается на некоторые паблики (например, статьи дополнительных описаний).

    Автоматизировать в этом месте можно до потери пульса. Тут главное не стараться ради стараний. Поэтому, если бы я выбирал, то остался с Документами Гугл на уровне вводного описания и абстракции.

    Пример проблемной автоматизации. Вот эта страница https://oshliaer.github.io/qna?target=labs/apps-sc... генерируется из Документа Гугл. И все бы ничего, но только вначале это был небольшой скрипт на 5 строк в Apps Script, а теперь это приложение на Go, которое еще и контрибьютить иногда хочется. Как вообще такое получилось!? я уже сказать не могу, но то, что это были большие переоцененные надежды на Документы Гугл - факт. Т.е. вы рискуете нарваться на автоматизацию того, что вам вообще не нужно.

    Я бы Jire ничего не предпочел, но добавил бы документацию REST в OpenAPI с предложениями на изменения через git с реверс ссылками на паблик, генерируемый в Confluence. Плюс такого подхода в полнейшей и тотальной уже готовой автоматизации (подкрутить пару Docker конфигов) вплоть до тестов, проверки типов и кросс-ссылок. Обратите внимание, что ссылки должны иметь общий характер. Или же необходимо просто встроить файлы репозитория в нужный контекст статьи.

    Применимо ли это к вашим задачам миграции - это отдельный вопрос. Возможно, там какие-то невероятные многоходовки, которые просто невозможно указать в комментариях в коде OpenAPI. Тогда тут нужен более системный подход. Возможно, хранение и связывание большого количества параметров через Гугл Таблицы (используя их как первоисточник) будет как-то оправдано.
    Ответ написан
    Комментировать
  • Как автоматически архивировать данные с одного листа таблицы на другой?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Необходимо воспользоваться поиском

    либо воспользоваться предлагаемыми на рынке услугами, см. контакты в профиле.
    Ответ написан
    Комментировать
  • Как ежедневно автоматически сохранять значение столбцов в Google Таблицах?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Как вариант

    const SETTINGS = {
      bookId: '1la6H9omAvlKuMkt-jwZGkp23K0wvV-9CXK9Kq1S0sTw',
      dataSheetNames: ['Лист1', 'Лист2'],
      archiveSheetName: ['Вывод данных'],
    };
    
    function createTrigger() {
      ScriptApp.getProjectTriggers().forEach(
        (trigger) =>
          trigger.getHandlerFunction() === 'saveData' &&
          trigger.getEventType() === ScriptApp.EventType.CLOCK &&
          (ScriptApp.deleteTrigger(trigger) || console.info(`Tirgger ${trigger.getUniqueId()} was deleted`))
      );
      ScriptApp.newTrigger('saveData').timeBased().atHour(17).everyDays(1).create();
    }
    function saveData() {
      const book = SpreadsheetApp.openById(SETTINGS.bookId);
    
      const date = new Date();
      const sheet = book.getSheetByName(SETTINGS.archiveSheetName);
    
      // console.log(book);
    
      SETTINGS.dataSheetNames.forEach((name) => {
        const sheetD = book.getSheetByName(name);
        const values = sheetD.getRange('A2:B').getValues();
        saveData_(sheet, date, values);
      });
      console.info(`saveData was called successful`);
    }
    
    /**
     * @param {globalThis.SpreadsheetApp.Sheet} sheet
     * @param {Date} x
     * @param {any[][]} values
     */
    function saveData_(sheet, x, values) {
      const tz = sheet.getParent().getSpreadsheetTimeZone();
      const x_ = Utilities.formatDate(x, tz, 'dd.MM.yyyy');
      const data = sheet.getDataRange().getValues();
      const ys = data.map((row) => row[0]);
      const xs = data[0].map((cell) => (cell && cell.getTime ? Utilities.formatDate(cell, tz, 'dd.MM.yyyy') : ''));
      let indexX = xs.indexOf(x_);
      if (indexX === -1) indexX = sheet.getLastColumn();
      const preData = data.map((row) => [row[indexX] || '']);
      preData[0][0] = preData[0][0] || x;
      console.log(preData);
      values.forEach((row) => {
        if (row[0] !== '') {
          const index = ys.indexOf(row[0]);
          if (index !== -1) {
            preData[index] = [preData[index][0] || row[1]];
          }
        }
      });
      console.log(JSON.stringify(preData));
    
      sheet.getRange(1, indexX + 1, preData.length, 1).setValues(preData);
    }


    Хоть мне и кажется, что я не продумал мелочи, т.к. подпись к полю на каждом листе должна быть уникальна. Но это можно поправить, автоматически добавляя имя листа, например.

    Пример в Таблице https://docs.google.com/spreadsheets/d/1la6H9omAvl...
    Ответ написан
    Комментировать
  • Как дописать цикл?

    oshliaer
    @oshliaer Куратор тега Google Apps Script
    Google Products Expert
    Вам нужно добавить данные о листе в цикл и из цикла вызывать генерацию файла

    function onOpen() {
      var ss = SpreadsheetApp.getActiveSpreadsheet();
      var csvMenuEntries = [{ name: 'export as csv files', functionName: 'userActionSaveAllSheetsAsCSV' }];
      ss.addMenu('csv', csvMenuEntries);
    }
    
    function userActionSaveAllSheetsAsCSV() {
      var ss = SpreadsheetApp.getActiveSpreadsheet();
      var sheets = ss.getSheets();
      var ssid = ss.getId();
    
      var fileInDrive = DriveApp.getFileById(ssid);
      var folderInDrive = fileInDrive.getParents().next().getId();
    
      const urls = sheets.map((sheet) => {
        const filename = `${sheet.getParent().getName()} ${sheet.getName()}.csv`;
        const array = sheet.getDataRange().getValues();
        const url = saveArrayToCSV_(array, filename, folderInDrive);
        return { url, filename, folderInDrive };
      });
    
      console.log(JSON.stringify(urls, null, '  '));
    }
    
    /**
     * @param {string[][]} array
     * @param {string} filename
     * @param {string} folderInDrive
     * @returns {string} The file url
     */
    function saveArrayToCSV_(array, filename, folderInDrive) {
      const data = array.map((row) => row.join(';')).join('\n');
      const url = DriveApp.getFolderById(folderInDrive)
        .createFile(filename, data, 'text/csv')
        .getDownloadUrl()
        .replace('?e=download&gd=true', '');
      return url;
    }


    Результатом работы программы являются файлы, а также можно получить лог с именами файлов, идентификатором папки и ссылками.

    620deb5b1701d349092522.png

    Ссылка на живой пример https://docs.google.com/spreadsheets/d/1A0ytCgO-v0...
    Ответ написан
    Комментировать
  • Почему не обновляет токен Google API?

    oshliaer
    @oshliaer Куратор тега Google Sheets
    Google Products Expert
    Проверьте, как вы сохраняете токен для обновления, он должен быть строкой без экранирующих символов

    1/xxxxxxxxxxxxxxx-ZZZ_yyyyyyyyyyyyy

    а не

    1\/xxxxxxxxxxxxxxx-ZZZ_yyyyyyyyyyyyy
    Ответ написан