Меню

Ошибка exception the starting row of the range is too small

var myPnum = dJoin.indexOf(appProfile);
var dataRow = ws.getRange(myPnum + 1, 1, 1, ws.getLastColumn()).getValues();

You don’t handle the case when appProfile is not in the dJoin array.

When this happens, the indexOf will return -1. So myPnum will equals -1. And so the first parameter of getRange will be -1 + 1 = 0 which is incorrect for Apps Script, it has to be 1 (= first line) or higher.

Retrieve the ID properly from your sheet

myPnum + 2 will not help you. The error will disappear but the problem is not there. The problem is how you retrieve the values from the sheet. You should not do join().split().

To retrieve all the IDs already present in your sheet, you need to do

let data = ws.getRange(2, 1, ws.getLastRow(), 1).getValues() // retrieve the first column, but not the header! and remove all possible empty cells
  .flat() // transform the array from [[...],[...]] to [...,...]
  .filter(cell => cell != '') // remove empty rows
let myPnum = data.indexOf(appProfile) // search your ID
if(myPnum == -1) {
  // then the ID doesnt exist in the sheet, deal with it
} else {
  // you can retrieve the corresponding row
  let fullRow = ws.getRange(myPnum + 2, 1, 1, ws.getLastColumn()).getValues();
  // do whatever you need to do...
}

I have this little script that’s giving me some trouble. I get the following error:

Error Exception: The starting row of the range is too small
appendToRecords @ Code.gs:10

What I’m trying to accomplish is to assign cell values(A list of names) from a specific range on one sheet after the last row of a specific column (F) in another sheet.

This is my current code:

function appendToRecords() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sourcesheet = ss.getSheetByName("Assignment");
  var targetSheet = ss.getSheetByName("Review")
  var reportData = sourcesheet.getRange("C4:C13")
                              .getValues();
  var owner = targetSheet.getRange("F1:F").getValues();
  var lastRow = owner.filter(String).lenght;
  //copy data
  targetSheet.getRange(lastRow + 1,1,10,1)
              .setValues(reportData);

};

Any help would be greatly appreciated!

asked Apr 6, 2022 at 21:48

AaronM's user avatar

1

There are several ways to solve this, depending on the details of your data.

The quick fix is to correct the typo in lenght. That will not work correctly, though, when column Review!F1:F contains blank values in between other values.

The better quick fix is to use Sheet.appendRow(), in a forEach loop.

The fix I would recommend, however, would be to use the the appendRows_() utility function.

answered Apr 7, 2022 at 9:38

doubleunary's user avatar

doubleunarydoubleunary

9,4671 gold badge7 silver badges39 bronze badges

1

Hello Dave,
I am less than a newbie to scripting.

Found your auto sync script and am trying to implement it for a cash flow sheet with columns titled (from left to right).

Title, Start Time, End Time(leaving blank for all day) are my columns.

I copy pasted your script and set my sheet and calendar time to Canada Eastern although the sheet sheet setting and calendar setting time zone for Toronto differs by an hour.
my Title column is a formula (=IF(ISBLANK(E2),D2&TEXT(F2,» $####.##»)&» Balance is «&TEXT(G2,»$#####.##»),D2&TEXT(E2,» $####.##»)&» Balance is «&TEXT(G2,»$#####.##»)).
Start time is date only and no time.
End date is blank to make it an all day event.

But I keep getting this error and am not able to debug.

error 1 — The starting column of the range is too small (When I run ‘update to cal’ from the sheet)
error 2 — The starting column of the range is too small. (line 333, file «Code») (when i run the script from the script page.

image

Any help and would be greatly appreciated.

Thanks
Jack

Я пытаюсь подключить HTML и Google Sheet. У меня ошибка, как показано ниже:

Exception: The starting row of the range is too small.

Вышеупомянутая ошибка указывает на скрипт приложений внутри Google Sheet — строка 10, как показано ниже:

var dataRow = ws.getRange(myPnum + 1, 1, 1, ws.getLastColumn()).getValues();

Может ли кто-нибудь помочь мне, как изменить коды, чтобы он работал?

Приведенные ниже коды пытаются отобразить HTML-страницу, чтобы пользователи могли ввести идентификационный номер, а затем получить последнюю строку связанных данных из Google Sheet. Спасибо.

Index.html:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <div class="form-row">
      <div class="form-group col-md-2">  
        <label for="idNum">ID Number</label>
        <input type="text" class="form-control" id="idNum">
        <output id="rnum"></output>
      </div>
     <button type="button" class="btn btn-outline-info" id="searchbtn">Search Profile</button>
   </div>
<script>
    document.getElementById("searchbtn").addEventListener("click", searchProfile);
   function searchProfile(){
     var appProfile = document.getElementById("idNum").value;
     if(appProfile.length === 6){
     google.script.run.withSuccessHandler(updateProfile).updateIdNum(appProfile);
     } 
     //else{
     //alert("invalid Profile Number");
     }   
   function updateProfile(returnData){
      document.getElementById("rnum").value = returnData[1];
   }
    </script>
  </body>
</html>

Скрипт приложений внутри Google Sheet:

function updateIdNum(appProfile){
  //  var appProfile = "101018"

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var ws = ss.getSheetByName("Results");

  var data = ws.getRange(1, 1, ws.getLastRow(), 1).getValues();
  var dJoin = data.join().split(",");
  var myPnum = dJoin.indexOf(appProfile);
  var dataRow = ws.getRange(myPnum + 1, 1, 1, ws.getLastColumn()).getValues();
  Logger.log(dataRow[0]);

  if (myPnum > -1){
    return dataRow[0];
  } else {
    Logger.log("unknown")
    //return "unavailable";
  } 
}


function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
  .createMenu('Custom Menu')
  .addItem('Show sidebar', 'showSidebar')
  .addToUi();
}

function showSidebar() {
  var html = HtmlService.createHtmlOutputFromFile('index')
  .setTitle('My custom sidebar')
  .setWidth(300);
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
  .showSidebar(html);
}

Данные Google Sheet выглядят так:

ID Number | First Name | Last Name|
101018    | John       | Doe      |
101011    | Jane       | Doe      |

1 ответ

var myPnum = dJoin.indexOf(appProfile);
var dataRow = ws.getRange(myPnum + 1, 1, 1, ws.getLastColumn()).getValues();

Вы не обрабатываете случай, когда appProfile не находится в массиве dJoin.

Когда это произойдет, indexOf вернет -1. Таким образом, myPnum будет равно -1. Итак, первым параметром getRange будет -1 + 1 = 0, что неверно для скрипта приложений, оно должно быть 1 (= первая строка) или выше.


1

ValLeNain
10 Ноя 2022 в 12:58

что это?

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

Когда Его Бросают

Учитывая массив, объявленный как:

 byte[] array = new byte[4];
 

Вы можете получить доступ к этому массиву от 0 до 3, значения за пределами этого диапазона приведут IndexOutOfRangeException к выбрасыванию. Помните об этом при создании массива и доступе к нему.

Длина Массива
В C#, как правило, массивы основаны на 0. Это означает, что первый элемент имеет индекс 0, а последний элемент имеет индекс Length - 1 (где Length общее количество элементов в массиве), поэтому этот код не работает:

 array[array.Length] = 0;
 

Кроме того, пожалуйста, обратите внимание, что если у вас есть многомерный массив, то вы не можете использовать Array.Length оба измерения, вы должны использовать Array.GetLength() :

 int[,] data = new int[10, 5];
for (int i=0; i < data.GetLength(0);   i) {
    for (int j=0; j < data.GetLength(1);   j) {
        data[i, j] = 1;
    }
}
 

Верхняя Граница Не Включает
В следующем примере мы создаем необработанный двумерный массив Color . Каждый элемент представляет собой пиксель, индексы-от (0, 0) до (imageWidth - 1, imageHeight - 1) .

 Color[,] pixels = new Color[imageWidth, imageHeight];
for (int x = 0; x <= imageWidth;   x) {
    for (int y = 0; y <= imageHeight;   y) {
        pixels[x, y] = backgroundColor;
    }
}
 

This code will then fail because array is 0-based and last (bottom-right) pixel in the image is pixels[imageWidth - 1, imageHeight - 1] :

 pixels[imageWidth, imageHeight] = Color.Black;
 

In another scenario you may get ArgumentOutOfRangeException for this code (for example if you’re using GetPixel method on a Bitmap class).

Arrays Do Not Grow
An array is fast. Very fast in linear search compared to every other collection. It is because items are contiguous in memory so memory address can be calculated (and increment is just an addition). No need to follow a node list, simple math! You pay this with a limitation: they can’t grow, if you need more elements you need to reallocate that array (this may take a relatively long time if old items must be copied to a new block). You resize them with Array.Resize<T>() , this example adds a new entry to an existing array:

 Array.Resize(ref array, array.Length   1);
 

Don’t forget that valid indices are from 0 to Length - 1 . If you simply try to assign an item at Length you’ll get IndexOutOfRangeException (this behavior may confuse you if you think they may increase with a syntax similar to Insert method of other collections).

Special Arrays With Custom Lower Bound
First item in arrays has always index 0. This is not always true because you can create an array with a custom lower bound:

 var array = Array.CreateInstance(typeof(byte), new int[] { 4 }, new int[] { 1 });
 

In that example, array indices are valid from 1 to 4. Of course, upper bound cannot be changed.

Wrong Arguments
If you access an array using unvalidated arguments (from user input or from function user) you may get this error:

 private static string[] RomanNumbers =
    new string[] { "I", "II", "III", "IV", "V" };

public static string Romanize(int number)
{
    return RomanNumbers[number];
}
 

Unexpected Results
This exception may be thrown for another reason too: by convention, many search functions will return -1 (nullables has been introduced with .NET 2.0 and anyway it’s also a well-known convention in use from many years) if they didn’t find anything. Let’s imagine you have an array of objects comparable with a string. You may think to write this code:

 // Items comparable with a string
Console.WriteLine("First item equals to 'Debug' is '{0}'.",
    myArray[Array.IndexOf(myArray, "Debug")]);

// Arbitrary objects
Console.WriteLine("First item equals to 'Debug' is '{0}'.",
    myArray[Array.FindIndex(myArray, x => x.Type == "Debug")]);
 

This will fail if no items in myArray will satisfy search condition because Array.IndexOf() will return -1 and then array access will throw.

Next example is a naive example to calculate occurrences of a given set of numbers (knowing maximum number and returning an array where item at index 0 represents number 0, items at index 1 represents number 1 and so on):

 static int[] CountOccurences(int maximum, IEnumerable<int> numbers) {
    int[] result = new int[maximum   1]; // Includes 0

    foreach (int number in numbers)
          result[number];

    return resu<
}
 

Конечно, это довольно ужасная реализация, но я хочу показать, что она не сработает для отрицательных чисел и чисел выше maximum .

Как это относится к List<T> ?

Те же случаи, что и массив — диапазон допустимых индексов — 0 ( List индексы всегда начинаются с 0) для list.Count доступа к элементам за пределами этого диапазона, вызовут исключение.

Обратите внимание, что List<T> броски ArgumentOutOfRangeException выполняются для тех же случаев, когда используются массивы IndexOutOfRangeException .

В отличие от массивов, List<T> запускается пустым — поэтому попытка доступа к элементам только что созданного списка приводит к этому исключению.

 var list = new List<int>();
 

Распространенным случаем является заполнение списка индексированием (аналогично Dictionary<int, T> ), что приведет к исключению:

 list[0] = 42; // exception
list.Add(42); // correct
 

IDataReader и столбцы
Представьте, что вы пытаетесь прочитать данные из базы данных с помощью этого кода:

 using (var connection = CreateConnection()) {
    using (var command = connection.CreateCommand()) {
        command.CommandText = "SELECT MyColumn1, MyColumn2 FROM MyTable";

        using (var reader = command.ExecuteReader()) {
            while (reader.Read()) {
                ProcessData(reader.GetString(2)); // Throws!
            }
        }
    }
}
 

GetString() выбросит, IndexOutOfRangeException потому что ваш набор данных содержит только два столбца, но вы пытаетесь получить значение из 3-го (индексы всегда основаны на 0).

Пожалуйста, обратите внимание, что это поведение является общим для большинства IDataReader реализаций ( SqlDataReader OleDbDataReader и так далее).

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

  var data = dr["Colum1"];  // Missing the n in Column1.
 

Это происходит потому, что оператор индексатора реализован при попытке получить индекс несуществующего поля Colum1. Метод GetOrdinal вызовет это исключение, когда его внутренний вспомогательный код вернет значение -1 в качестве индекса «Colum1».

Прочее
Существует еще один (задокументированный) случай, когда возникает это исключение: если DataView имя столбца данных, указанное в DataViewSort свойстве , недопустимо.

Как избежать

В этом примере позвольте мне для простоты предположить, что массивы всегда одномерны и основаны на 0. Если вы хотите быть строгим (или вы разрабатываете библиотеку), вам, возможно, потребуется заменить 0 на GetLowerBound(0) и .Length с GetUpperBound(0) (конечно, если у вас есть параметры типа System.Arra y, это не применимо T[] ). Пожалуйста, обратите внимание, что в этом случае верхняя граница включена, тогда этот код:

 for (int i=0; i < array.Length;   i) { }
 

Должно быть переписано вот так:

 for (int i=array.GetLowerBound(0); i <= array.GetUpperBound(0);   i) { }
 

Пожалуйста, обратите внимание, что это запрещено (это приведет InvalidCastException к сбою), поэтому, если ваши параметры T[] соответствуют, вы можете быть уверены в пользовательских массивах с нижней границей:

 void foo<T>(T[] array) { }

void test() {
    // This will throw InvalidCastException, cannot convert Int32[] to Int32[*]
    foo((int)Array.CreateInstance(typeof(int), new int[] { 1 }, new int[] { 1 }));
}
 

Validate Parameters
If index comes from a parameter you should always validate them (throwing appropriate ArgumentException or ArgumentOutOfRangeException ). In the next example, wrong parameters may cause IndexOutOfRangeException , users of this function may expect this because they’re passing an array but it’s not always so obvious. I’d suggest to always validate parameters for public functions:

 static void SetRange<T>(T[] array, int from, int length, Func<i, T> function)
{
    if (from < 0 || from>= array.Length)
        throw new ArgumentOutOfRangeException("from");

    if (length < 0)
        throw new ArgumentOutOfRangeException("length");

    if (from   length > array.Length)
        throw new ArgumentException("...");

    for (int i=from; i < from   length;   i)
        array[i] = function(i);
}
 

If function is private you may simply replace if logic with Debug.Assert() :

 Debug.Assert(from >= 0 amp;amp; from < array.Length);
 

Check Object State
Array index may not come directly from a parameter. It may be part of object state. In general is always a good practice to validate object state (by itself and with function parameters, if needed). You can use Debug.Assert() , throw a proper exception (more descriptive about the problem) or handle that like in this example:

 class Table {
    public int SelectedIndex { get; set; }
    public Row[] Rows { get; set; }

    public Row SelectedRow {
        get {
            if (Rows == null)
                throw new InvalidOperationException("...");

            // No or wrong selection, here we just return null for
            // this case (it may be the reason we use this property
            // instead of direct access)
            if (SelectedIndex < 0 || SelectedIndex >= Rows.Length)
                return null;

            return Rows[SelectedIndex];
        }
}
 

Validate Return Values
In one of previous examples we directly used Array.IndexOf() return value. If we know it may fail then it’s better to handle that case:

 int index = myArray[Array.IndexOf(myArray, "Debug");
if (index != -1) { } else { }
 

How to Debug

In my opinion, most of the questions, here on SO, about this error can be simply avoided. The time you spend to write a proper question (with a small working example and a small explanation) could easily much more than the time you’ll need to debug your code. First of all, read this Eric Lippert’s blog post about debugging of small programs, I won’t repeat his words here but it’s absolutely a must read.

You have source code, you have exception message with a stack trace. Go there, pick right line number and you’ll see:

 array[index] = newValue;
 

You found your error, check how index increases. Is it right? Check how array is allocated, is coherent with how index increases? Is it right according to your specifications? If you answer yes to all these questions, then you’ll find good help here on StackOverflow but please first check for that by yourself. You’ll save your own time!

A good start point is to always use assertions and to validate inputs. You may even want to use code contracts. When something went wrong and you can’t figure out what happens with a quick look at your code then you have to resort to an old friend: debugger. Just run your application in debug inside Visual Studio (or your favorite IDE), you’ll see exactly which line throws this exception, which array is involved and which index you’re trying to use. Really, 99% of the times you’ll solve it by yourself in a few minutes.

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

В VB.NET сторона этой истории

Все, что мы сказали в ответе на C#, справедливо для VB.NET с очевидными различиями в синтаксисе, но есть важный момент, который следует учитывать, когда вы имеете дело с VB.NET массивы.

В VB.NET, массивы объявляются, устанавливая максимальное допустимое значение индекса для массива. Это не количество элементов, которые мы хотим сохранить в массиве.

 ' declares an array with space for 5 integer 
' 4 is the maximum valid index starting from 0 to 4
Dim myArray(4) as Integer
 

Таким образом, этот цикл заполнит массив 5 целыми числами, не вызывая исключения IndexOutOfRangeException

 For i As Integer = 0 To 4
    myArray(i) = i
Next
 

В VB.NET правило

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

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Ошибка exception processing message c0000013 parameters 75b3bf7c 4 75b3bf7c
  • Ошибка exception processing message 0xc0000013 unexpected parameters