«не удается прочитать свойство ‘indexOf’ неопределенного»

#javascript #google-apps-script #google-sheets #indexof

#javascript #google-apps-script #google-sheets #indexof

Вопрос:

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

 function UpdateProjects()
{

//----------EDIT THIS FOR A DIFFERENT CLIENT-------------
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("Projects");
  var etilastrow = sheet.getLastRow();

  var urlRange = sheet.getRange(2, 1, etilastrow);
  var urlsvalues = urlRange.getValues();
  var urls = urlRange.getFormulas();
  Logger.log(urls)

  //looping through all of the rows
  for (var i = 0; i < etilastrow;   i) {
   currentRow = 2   i;
  var dirturl = urls[i];
  var remove_before = dirturl[i].indexOf('",');
    var url = dirturl[i].substring(12, remove_before);

Logger.log(currentRow);
    Logger.log(url);

//-----------------Loop code below until last project-------
  var projectPCBdesignsheet = SpreadsheetApp.openByUrl(url).getSheetByName("Main");
    var lstrow = projectPCBdesignsheet.getLastRow(); 

  const vA=projectPCBdesignsheet.getRange(7,2,lstrow).getValues();//get data from project's PCB Design sheet
   
//add last edit values  
  sheet.getRange(currentRow, 4).setValue(vA[10]);
  sheet.getRange(currentRow, 5).setValue(vA[11]);    

//additional data from PCB design sheet
  sheet.getRange(currentRow, 6).setValue(vA[0]);
  sheet.getRange(currentRow, 7).setValue(vA[1]);
  sheet.getRange(currentRow, 8).setValue(vA[2]);
  sheet.getRange(currentRow, 9).setValue(vA[3]);  
  sheet.getRange(currentRow, 11).setValue(vA[5]); 
}
} 

Скрипт работает, если я жестко закодирую URL-адрес в переменную. Прямо сейчас, из журнала, я вижу номер строки и чистый URL.

Проблема в том, что скрипт останавливается с этой ошибкой: «Ошибка типа: не удается прочитать свойство ‘indexOf’ неопределенного ETIUpdateProjects @ UpdateProjects.gs:18»

Я не понимаю, как он может найти ошибку в строке 18, если он показывает мне журнал currentRow и url, которые идут после строки «indexOf».

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

1. etilastrow не следует использовать таким образом, потому что вы начинаете получать строки со 2-го, а не с 1-го, можете ли вы вычесть из него и посмотреть, поможет ли это?

Ответ №1:

Проблемы:

Проблема 1:

Начиная со второй строки, вы получаете одну дополнительную пустую ячейку в данных:

 var urlRange = sheet.getRange(2, 1, etilastrow);
 

это даст вам что-то вроде: [[=link],['']]

это можно исправить, изменив etilastrow на etilastrow-1 .

и то же самое в for цикле.

Проблема 2:

Даже если вы исправите проблему 1, основная проблема в текущем коде, которая также вызывает ошибку, которую вы получаете, находится в этих строках:

 var dirturl = urls[i];
var remove_before = dirturl[i].indexOf('",');
 

Помните, urls что это массив etilastrow строк, но только один столбец. Если вы перефразируете свой код, то то, что вы вызываете, по сути urls[i][i] . Это вернется undefined после второй итерации, когда i станет 1 , потому что в данных есть только один столбец.

Причина, по которой ваш код работает, заключается в том, что вы тестируете его с заполненной одной ячейкой и, следовательно urls[0][0] , работает правильно, но если вы добавите больше URL-адресов, второй индекс увеличится до более чем 0 , и это, например urls[0][1] , приведет к удалению и ошибке, потому urls что имеет только один столбец.

Чтобы решить эту проблему, вы могли бы изменить:

 var dirturl = urls[i];
var remove_before = dirturl[i].indexOf('",');
var url = dirturl[i].substring(12, remove_before);
 

Для:

 var dirturl = urls[i];
var remove_before = dirturl[0].indexOf('",');
var url = dirturl[0].substring(12, remove_before);
 

Улучшения:

Улучшение 1:

Я бы посоветовал вам ознакомиться с for циклами, потому что они могут сбивать с толку при работе с индексами и массивами, и вам нужно быть достаточно удобным для их обработки. Используйте forEach и flat() для прямой работы с каждым URL-адресом:

   var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("Projects");
  var etilastrow = sheet.getLastRow();

  var urlRange = sheet.getRange(2, 1, etilastrow-1);
  var urlsvalues = urlRange.getValues();
  var urls = urlRange.getFormulas();


  urls.flat().forEach((r,i)=>{
    let currentRow = i   2;
    let remove_before = r.indexOf('",');
    let url = r.substring(12, remove_before);
    console.log(url)
    console.log(currentRow)
    // put here the rest of your code
  })
 

Улучшение 2:

С точки зрения Google Apps Script setValue многократный вызов, но также и в for цикле, может стоить большой производительности. Согласно рекомендациям, правильный способ сделать это — сохранить значения в массиве, а затем использовать setValues вместо этого вне цикла for. Таким образом, вы используете только setValues один раз.

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

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

1. Мариос, большое спасибо за вашу помощь и за то, что вы выделили для этого некоторое время. Я внес некоторые изменения в соответствии с вашими советами, а также обновил setValue одним setValues. Скрипт работает быстро и хорошо. Есть небольшая проблема: цикл forEach не завершится, пока не найдет пустую ячейку без URL. Это через «недопустимый аргумент: url». Я заметил, что невозможно вернуть, прервать или продолжить цикл такого типа, поэтому я полагаю, что это невозможно решить с помощью if (url == «»). Еще раз спасибо за вашу поддержку. Действительно, действительно ценится.

2. К вашему сведению, я также пытался запустить этот скрипт, когда пользователь выбирает этот конкретный лист (информация будет обновляться, когда это необходимо). Мои базовые навыки работы с GAS приводят к такому сценарию, который, к сожалению, не работает. функция onSelectionChange(e) { // Запуск сценария обновления при выборе листа var sheet = e.sheet; var ssname = SpreadsheetApp.getActiveSheet().getName(); if(ssname === «Projects») { UpdateProjects(); } }

3. @Andre8426 рад, что ваша проблема была решена. forEach завершится в конце массива. Если вы не хотите завершать работу до этого, я боюсь, вам нужно использовать for цикл. С точки зрения производительности вы ничего не теряете с циклом for, поэтому, если вы хотите break или continue мой совет — продолжать использовать цикл for.

4. @Andre8426 Я бы посоветовал вам опубликовать новый вопрос относительно модификации. Stackoverflow не допускает последующих вопросов. Извините за неудобства.