Удаление дубликатов в Google Таблицах (скрипт) занимает слишком много времени для обработки

#javascript #google-apps-script #google-sheets #duplicates

#javascript #google-apps-script #google-sheets #дубликаты

Вопрос:

Я пытаюсь дедуплицировать весь свой лист, и скрипт работает нормально, однако для запуска требуется более 60 секунд. Я слишком усложняю это, и действительно ли есть более простой код, чтобы получить то, что мне нужно? Просто кажется, что для обработки такой простой задачи требуется много времени.

Мои данные находятся только между 4-12 тыс. строк.

 function removeDuplicates() {
 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
 var data = sheet.getDataRange().getValues();
 var newData = [];
 for (var i in data) {
   var row = data[i];
   var duplicate = false;
   for (var j in newData) {
    if (row.join() == newData[j].join()) {
     duplicate = true;
    }
  }
//If not a duplicate, put in newData array
 if (!duplicate) {
  newData.push(row);
 }
}
//Delete the old Sheet and insert the newData array
 sheet.clearContents();
 sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
 

Ответ №1:

Если вы используете объект, вы значительно сократите количество итераций.

 function removeDuplicates() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  const data = sheet.getDataRange().getValues();
  
  let newDataObject = {};
  for (let row of data) {
    newDataObject[row.join()] = row;
  }
  const newData = Object.values(newDataObject);
  
  // Clear the old Sheet and insert the newData array
  sheet.clearContents();
  sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
 

Ответ №2:

В качестве другого подхода, как насчет использования метода removeDuplicates() ? Когда ваш скрипт модифицируется, он становится следующим.

Модифицированный скрипт:

 function removeDuplicates() {
  SpreadsheetApp.getActiveSpreadsheet().getSheets()[1].getDataRange().removeDuplicates();
}
 

Ссылка:

Ответ №3:

Теперь, когда у нас есть версия 8, вы можете использовать класс. Set Не предпринимал никаких попыток сравнить производительность, поэтому я не знаю, будете ли вы справедливее, когда речь идет о скорости выполнения, хотя код гораздо более удобочитаем. Попробуйте следующее и расскажите мне, как это происходит:

 // V8 runtime version using Set
function removeDuplicates(sheetName) {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
    var rows = sheet.getDataRange().getValues();

    var dedupedValues = [];
    var set = new Set();

    rows.forEach(function(row) {
        let key = row.join();

        if (set.has(key)) return;

        set.add(key);
        dedupedValues.push(row);

    });

    //Delete the old Sheet and insert the dedupedValues array
    sheet.clearContents();
    sheet.getRange(1, 1, dedupedValues.length, dedupedValues[0].length).setValues(dedupedValues);
}
 

Если вам не нравится версия 8, вы можете сделать то же самое, используя решение @Diego…но с несколькими изменениями следующим образом:

 // ES5 version using object keys
function removeDuplicates(sheetName) {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
    var rows = sheet.getDataRange().getValues();

    var dedupedValues = [];
    var keys = {};

    rows.forEach(function(row) {
        var key = row.join();

        if (key in keys) return;

        keys[key] = true;
        dedupedValues.push(row);

    });

    //Delete the old Sheet and insert the dedupedValues array
    sheet.clearContents();
    sheet.getRange(1, 1, dedupedValues.length, dedupedValues[0].length).setValues(dedupedValues);
}

 

Комментарии:

1. Этот вопрос касается производительности, а не общего альтернативного кодирования. Например, простой цикл for быстрее, чем forEach .

2. Первое решение в вашем ответе очень полезно, если кто-то заинтересован в сравнении строк на основе результатов функции столбцов строк, потому что вы можете сохранить набор для сравнения и набор для конечного выходного массива, а затем повторно создать выходной массив с помощью оператора распространения. И тогда вам не нужно использовать dedupedValues.push(row) Вы можете просто использовать [...set]

Ответ №4:

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я не знаю API Google Sheets.

Я предложил несколько улучшений и добавил комментарии в код. Большое значение в производительности имеет кэширование. Поэтому не делайте что-то дважды, если в этом нет необходимости (или DRY = Не повторяйтесь!). Если вы забыли свой кошелек и вам снова нужно подняться наверх, это удваивает время, затрачиваемое на то, чтобы покинуть парадную дверь. То же самое с кодом.

Если вы, как правило, заинтересованы в повышении производительности (и все должны быть) Я предлагаю вам взглянуть на то, как написать эффективный JavaScript — Марк Надаль

 function removeDuplicates() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
  var data = sheet.getDataRange().getValues();
  var newData = [];
  var newDataJoinedCache = {};

  // help the loop to not have to read the same values over and over again
  // if you prepare as much data as possible for the loop it will thankfully speed up
  for (var i = 0, len = data.length; i < len; i  ) {
    // data won’t change in the loop, so we can cache it here once(!)
    // as you can see, I added a semicolon to prevent unwanted results
    // imagine a row with colums [ "a", "bc"] and another one with [ "ab", "c"]
    // just joined without separator they will both be the same "abc", which is wrong
    var joinedRow = data[i].join(";");

    // no need as we will know it with one simple comparison
    // var duplicate = false;

    // Just make one simple comparison
    // instead of over and over joining the arrays in newData just cache them

    if (!newDataJoinedCache[joinedRow]) {
      newData.push(data[i]);
      // we push the joined string as a cache
      newDataJoinedCache[joinedRow] = true;
    }
  }
  //Delete the old Sheet and insert the newData array
  sheet.clearContents();
  sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
 

Дайте мне знать, как это работает и сколько времени это сэкономит в сравнении.

Комментарии:

1. Это заняло от 60 секунд в среднем до 10-15 секунд … некоторые были 3-4 секунды. Это потрясающе! Спасибо за ваше понимание!

2. Рад слышать 👍