Обновите или создайте новую строку на основе уникального идентификатора Google Sheets

Я нашел несколько вариантов, которые близки к тому, что я хочу делать, но ничего не соответствовали бы ему.


Запрос достаточно простой.

Лист A - мастер-лист (имеет 1 строку заголовка)

Лист B - входной лист (имеет 1 строку заголовка)

Столбец C - уникальный идентификатор (один и тот же столбец на обоих листах)


Триггер

  • Лист B редактируется

Действия

  • Скрипт находит уникальный идентификатор на листе B в столбце C и ищет его на листе A в столбце C.
  • Если он его находит, вся соответствующая строка на листе A заменяется всей соответствующей строкой с листа B.
  • Если он не находит ее, новая строка добавляется внизу листа A, а вся соответствующая строка из листа B добавляется как новая запись в новую строку внизу листа A.
  • Вся соответствующая строка на листе B удалена.

Действия повторяются до тех пор, пока на листе B не останется заполненных строк, начиная со строки 2 (т.е. исключая заголовок строки 1).

Спасибо

Изменения

  1. Чтобы прояснить, почему я хочу это сделать. У меня есть форма, которая отправляется и отправляет данные в Google Таблицы (Cognito - ›Zapier -› Google Sheets). Часть этой формы включает повторяющиеся разделы (позиции). Текущий метод, который импортирует ответы, не имеет проблем с правильным добавлением новых ответов, однако при обновлении ответа он не может правильно найти / обновить существующие строки для повторяющихся разделов. Таким образом, у меня было намерение использовать лист A в качестве основного листа, а затем использовать лист B просто как принимающий лист. Таким образом, я могу просто отправить каждую запись (включая обновленные) как новую запись на листе B, а затем мой скрипт выполнит обновление.
  2. Лист B будет редактироваться автоматически каждый раз при отправке или обновлении новой записи формы. Редактирование - это, по сути, добавление новой строки и внесение данных в эту строку. Может быть хорошей идеей добавить к триггеру 1-минутный таймер, чтобы при добавлении большого количества данных у него было время для этого.
  3. Я даже отдаленно не близок к эксперту по сценариям. Я просто просматриваю разные сценарии, написанные другими людьми, и пытаюсь объединить их, чтобы заставить их работать для того, что мне нужно. Я нашел сценарии, которые перемещают строку, а затем удаляют ее, но не проверяют соответствие значений для обновления. Я нашел другие сценарии, которые проверяют уникальные значения и копируют их, но они не удаляют исходную строку на другом листе. Я пытался объединить их, но, поскольку у меня нет базовых знаний, я не могу заставить его работать.

person Rylan Grose    schedule 20.01.2021    source источник
comment
Это возможно с onChange или onEdit. Вероятно, onEdit проще, потому что в объекте события есть больше информации о вашем текущем местоположении.   -  person Cooper    schedule 21.01.2021
comment
Я не могу понять твою цель. Прошу прощения за это. Могу я спросить вас о вашей цели? 1. Насчет Sheet B is edited, как вы его отредактируете? Например, значения, включающие несколько строк, копируются и вставляются? 2. Насчет The entire respective row on Sheet B is Deleted., в этом случае лист B всегда имеет только одну строку заголовка? В этой ситуации значения помещаются на Лист B? 3. Могу я спросить вас о текущем выпуске вашего сценария?   -  person Tanaike    schedule 21.01.2021
comment
Когда вы упомянули, что лист B и лист A имеют общий столбец C, я первой подумал, почему бы вам просто не вставить новую запись, но оказалось, что вы обрабатываете лист B - столбец C как очередь. вы можете это подтвердить? что ты уже испробовал?   -  person Jose Vasquez    schedule 21.01.2021
comment
Спасибо за вопросы. Я отвечу им, отредактировав свой вопрос, поскольку 500 символов в этом ответе будет недостаточно.   -  person Rylan Grose    schedule 21.01.2021
comment
Было бы полезно для сообщества, если бы у нас был тип данных, которые у вас есть на листах B и A. Можете ли вы поделиться аналогичной таблицей без конфиденциальных данных?   -  person Jose Vasquez    schedule 09.02.2021
comment
@JoseVasquez - Моя рабочая учетная запись не позволяет мне публиковать листы. Код, который я буду искать, в любом случае должен быть универсальным, поскольку я фактически копирую его на несколько разных листов, которые фиксируют различную информацию. Образец листа может быть буквально любым, единственное требование - лист A и лист B должны иметь совпадающие строки заголовка. Содержащиеся данные могут охватывать все типы данных, которые допускаются в Google Таблицах (даты, текст, числа, буквенно-цифровые значения, валюта, процент, время и т. Д.). Единственное, что я могу подтвердить, это то, что уникальный идентификатор всегда будет состоять из 9 цифр.   -  person Rylan Grose    schedule 10.02.2021


Ответы (1)


В качестве обходного пути я бы использовал простой триггер onEdit и поиск O (n)

Вот мой подход:

function onEdit(e) {
  // If it's not the Sheet B it won't make changes
  if (e.range.getSheet().getName() !== "Sheet B") {
    return;
  }

  var range = e.range;
  var numberRow = range.getA1Notation().slice(1);
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheetA = ss.getSheetByName("Sheet A");
  var sheetB = ss.getSheetByName("Sheet B");

  var currentRowB = sheetB.getRange(`A${numberRow}:D${numberRow}`);
  var id = currentRowB.getValues()[0][2];
  // There's to be 4 values in the row (no empty values)
  if(currentRowB.getValues()[0].filter(value => value !== '').length === 4) {

    // Get all the values in Sheet A
    var rows = sheetA.getDataRange().getValues();
    for (row=1; row < rows.length; row++) {
      // If column C matches the ID replace the row
      if(rows[row][2] === id) {
        var currentRowA = sheetA.getRange(`A${row+1}:D${row+1}`);
        currentRowA.setValues(currentRowB.getValues());
        currentRowB.deleteCells(SpreadsheetApp.Dimension.COLUMNS);
        return;
      }
    }

    // If the ID doesn't match then insert a new row
    var newRow = sheetA.getRange(`A${rows.length+1}:D${rows.length+1}`);
    newRow.setValues(currentRowB.getValues());
    currentRowB.deleteCells(SpreadsheetApp.Dimension.COLUMNS);
  }
}

Что соответствует заданным вами требованиям:

  • Скрипт находит уникальный идентификатор на листе B в столбце C и ищет его на листе A в столбце C. (строки 19–28)
  • Если он его находит, вся соответствующая строка на листе A заменяется всей соответствующей строкой с листа B. (строки 22-24)
  • Если он не находит его, новая строка добавляется внизу листа A, а вся соответствующая строка из листа B добавляется как новая запись в новую строку внизу листа A. (строка 31- 33)
  • Вся соответствующая строка на листе B удалена. (строки 22 и 33)

Я использовал этот формат листа в качестве примера:

введите описание изображения здесь

Оба листа имеют одинаковый формат. Имейте в виду, что этот сценарий проверяет наличие действительной строки (в данном конкретном случае 4 столбца, составляющих строку) перед ее заменой.

Как другой подход (обработка пустых данных)

Таким образом, этот сценарий должен запускаться каждые X минут или в то время, которое вы хотите, не имеет значения, поступают ли новые данные, потому что этот код будет обрабатывать все данные за определенное время.

Я отредактировал код, чтобы использовать ячейку Z1 в качестве ячейки блокировки и триггера на основе времени:

Триггер:

введите описание изображения здесь

Код

function processCells() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheetA = ss.getSheetByName("Sheet A");
  var sheetB = ss.getSheetByName("Sheet B");

  // If it's not the Sheet B or if there's a process running it won't make changes
  if (sheetB.getName() !== "Sheet B" || sheetB.getRange("Z1") === "Running") {
    return;
  }

  // Use the Z1 cell in order to block or unblock this sheet
  sheetB.getRange("Z1").setValue('Running');

  // Process all the rows
  var numCells = sheetB.getDataRange().getValues().length + 1;
  for (numberRow = 2; numberRow <= numCells; numberRow++) {

    var currentRowB = sheetB.getRange(`A${numberRow}:D${numberRow}`);
    var id = currentRowB.getValues()[0][2];

    // Get all the values in Sheet A
    var rows = sheetA.getDataRange().getValues();
    var match = false;
    for (row=1; row < rows.length; row++) {
      // If column C matches the ID replace the row
      if(rows[row][2] === id) {
        var currentRowA = sheetA.getRange(`A${row+1}:D${row+1}`);
        currentRowA.setValues(currentRowB.getValues());
        currentRowB.deleteCells(SpreadsheetApp.Dimension.COLUMNS);
        match = true;
        break;
      }
    }

    if(!match) {
      // If the ID doesn't match then insert a new row
      var newRow = sheetA.getRange(`A${rows.length+1}:D${rows.length+1}`);
      newRow.setValues(currentRowB.getValues());
      currentRowB.deleteCells(SpreadsheetApp.Dimension.COLUMNS);
    }
  }

  sheetB.getRange("Z1").setValue('');
}

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

введите описание изображения здесь

использованная литература

person Jose Vasquez    schedule 11.02.2021
comment
Привет, @Jose, я провел быстрый тест, и он выдает ошибку. TypeError: Cannot read property 'range' of undefined (onEdit @ Code.gs:3). Я удостоверился, что имена моих листов точно соответствуют Листу A и Листу B, и единственное, что я изменил в вашем коде, - это с 4 столбцов на 6 вместо 6 в строке 16. На этом примечании тоже: в строке должно быть 4 значения (не пусто ценности). Означает ли это, что у меня не может быть пустой точки данных? Итак, на вашем скриншоте, если бы в строке 3 ничего не было в столбце B, этот код не сработал? - person Rylan Grose; 11.02.2021
comment
Здравствуйте, поэтому я попросил в качестве примера Sheet, потому что код зависит от того, как данные INPUT. 4 значения - это всего лишь пример, в котором может быть 5 или более столбцов. Что касается не удается прочитать свойство, ошибка может быть связана с тем, что вы пытаетесь запустить код из редактора, а не редактировать ячейку (имейте в виду, что это простой код триггера). Если вы хотите запустить этот скрипт вручную после ввода данных, дайте мне знать, чтобы я мог изменить скрипт. Как вы упомянули, ваш ввод может содержать пустые данные, было бы лучше, если бы вам пришлось запускать этот код вручную (через API). - person Jose Vasquez; 11.02.2021
comment
1/2 Обычно, когда у меня есть код, который работает в целом, я могу достаточно хорошо настроить его, чтобы он работал с моим форматом листа. Не жаловался на корректировку количества столбцов, просто упоминал об этом на случай, если что-то случайно сломаю. Я тестировал с редактированием, но он не работал, но я просто сделал это снова, и триггер сработал. Я все еще пытаюсь понять, почему это не работает. Если я копирую / вставляю целую строку в лист B, функция не сообщает, что снова не было события onEdit. Это просто потому, что я копирую / вставляю, или потому что вводится сразу вся строка? - person Rylan Grose; 11.02.2021
comment
2/2 - Я также тестировал сценарий с пустыми данными, и он не сработал. Он по-прежнему должен работать так же, я просто подчеркивал, что вся строка не всегда будет заполнена. - person Rylan Grose; 11.02.2021
comment
Я только что отредактировал - person Jose Vasquez; 15.02.2021
comment
Еще не проводил невероятно подробное тестирование, но, похоже, пока он отлично работает по всем базовым требованиям (новые / повторяющиеся / пустые ячейки). Большое спасибо!!! - person Rylan Grose; 16.02.2021