Index was out of range error while exporting to Excel - c#

I am trying to export data from c# to excel using the following code:
enter worksheet = workbook.ActiveSheet;
worksheet.Name = "ExportedFromDatGrid";
//Loop through each row and read value from each column.
for (int i = 0; i < dataGridView1.Rows.Count + 1; i++)
{
worksheet.Cells[1, i] = dataGridView1.Columns[i - 1].HeaderText;
}
for (int i = 0; i < dataGridView1.Columns.Count; i++)
{
for (int j = 0; j < dataGridView1.Columns.Count - 1; j++)
{
// Excel index starts from 1,1. As first Row would have the Column headers,
// adding a condition check.
worksheet.Cells[i + 2, j + 1] = dataGridView1.Rows[i].Cells[j].Value.ToString();
}
}
I get the following error:
Index was out of range.Must be non negative and less than the size of the collection. Parameter name: index.
UPDATE I solved the problem by changing this for statement:
for ( int i = -1; i < DataGridView1.Columns.Count; i++)

I think the problem is that many online guides and tutorials exlpain that when you count through Lists<>, Arrays and rows/columns of a Table you need to add +1 because all these object containers have a start index of 0.
As a newcomer it might be hard to figure out at the beginning where you have to place the +1 and especially when you have to. Maybe you were confused because you wanted the total amount of rows as your max definition of i. But as you start your loop with int i = 0 (what is correct, because you dont want to skip the row with the index 0) you start as well at the point 0 and not 1. So there is no need to add +1 to the max breakpoint, because you still go dataGridView1.Rows.Count times (<-- amount how often your loop gets executed) through the rows.
This exception Index was out of range tells you that you wanted to do something with a row, which was out of range. It was out of range because this row's item didnt exist. Let´s say you have 10 rows with the index 0 - 9. Now you start going through them beginning at 0. So after 10 times executing, you went through rows 0 - 9. As dataGridView1.Rows.Count gives you the total amount of rows, in this example 10. But you set as the breakpoint dataGridView1.Rows.Count + 1 so the loop wants to do your task the 11th time with the row that has the index 10, but the index of your last row is 9. So it can't find this row and thats the situation when it gives you the Index out of range execption. Now I hope you understand what went wrong and why.

Try:
sheet.GetRow(rowNumber).CreateCell(columnNumber);
And then fill the cell value.

Related

Loop on datatable after deleting columns in C#

I am trying to delete rows from a datatable that have an empty or null cell, at the same time I check if a column has empty cells exceeding a percentage, if it's the case I drop the whole column. I tried proceeding like so:
private DataTable CleanData()
{
var dt = BindData(openFileDialog1.FileName);
for (var j = dt.Columns.Count-1; j >= 0; j--)
{
short count = 0;
for (var i = dt.Rows.Count - 1; i >= 0; i--)
{
if (!string.IsNullOrEmpty(dt.Rows[i][j].ToString())) continue;
count++;
}
var percentage = count * 100.0 / dt.Rows.Count;
if (percentage > 10)
{
dt.Columns.RemoveAt(j);
textFile.Text += " " + j + " ";
}
}
dt.AcceptChanges();
for (var j = dt.Columns.Count - 1; j >= 0; j--)
for (var i = dt.Rows.Count - 1; i >= 0; i--)
{
if (!string.IsNullOrEmpty(dt.Rows[i][j].ToString())) continue;
dt.Rows[i].Delete();
}
dt.AcceptChanges();
return dt;
}
I loop a first time over the datatable cells, then check the percentage of empty cells in a column and if it exceeds 10% I delete that column, then I loop a second time and this time delete each row that has an empty cell, but on the second loop I get an error message (System.Data.DeletedRowInaccessibleException) when it reaches a deleted column index, even though it's supposed to loop on a datatable where those columns aren't there.
Any clue where I messed up ?
Edit: I made the changes proposed but still getting the same error
What I THINK you are running into is an unexpected side-effect of your loop checking % and deleting columns. You are starting with the 0-index column (1st column). Checking and then deleting if empty. Do it in reverse... start with the LAST column and work back to 0 and here is why.
Say you start with a table of 3 columns, so your loop counter is intended to to 0, 1, 2. First cycle through, loop counter 0. You determine data good, no delete. Counter = 1 (2nd column). Determine it needs to be removed due to % empty. Now you delete column[1]. This moves what WAS column[2] and now becomes column[1] and your counter now advances to 2. You never checked what WAS the third column.
If you did in reverse, you start at column[3], check it, find its ok (or not, dont care). Now down 1 to column[2] and determine to remove. So it gets deleted and column[3] is now column[2]. Now you check column[0] and finish no problem.
You are already doing this when checking the ROWS (starting at the end and working back). Same principle applies.
As for your loop on deleting the ROW, I would invert your loops.
Outer loop per ROW (last row first, working back)
{
Inner loop per COLUMN
{
if any single column qualifies to delete the row
{
dt.rows[i].Delete();
break; [break out of the column checking loop]
}
}
[ continue with each ROW]
}
Since your existing outer loop is per column, if you process column 1 and delete row 5, then get to column 2 and try to delete row 5 again, that is your failure.
By checking all columns for a single row FIRST and getting out as soon as one qualifies for deletion, you are done with that row and never need to consider looking at any other columns. Move to the next row for processing.

How do I get a for loop to print out asterisks that form a pyramid with odd numbered rows?

I'm learning the for loop and as I understand it works this way: first it initializes the first statement, then it checks the condition, does the things in the body and then iterates. But I don't seem to get how this code works.
Here's the code:
using System;
class MainClass {
public static void Main (string[] args) {
for(int row=0;row<7;row++){
for(int num=0;2*num<13;num++){
Console.Write("*");
}
Console.WriteLine();
}
}
}
It was supposed to print this:
*
***
*****
*******
*********
***********
*************
Istead it prints
*******
*******
*******
*******
*******
*******
*******
Thanks in advance!
The main issue was not using the row variable in the condition check of the inner for loop, since the number of asterisks in each row is related to the row value.
The original logic does the same thing in every inner for loop, every time. 2*num<13 is basically the same as num<6.5, or really num<7 for ints, meaning 7 asterisks are printed for each row.
int numberOfRows = 7;
for (int row = 0; row < numberOfRows; row++)
{
for (int num = 0; num < 2 * row + 1; num++)
{
Console.Write("*");
}
Console.WriteLine();
}
FWIW, the only reason I even answered is to provide an example that's easier to read and maintain than the other answers. For example, here the row variable is named appropriately, illustrated by the use of a variable to contain the number of rows. If you want more rows, change numberOfRows. If you want a different number of asterisks on each row, change the condition in the inner for loop.
When dealing with for loops, it's a good idea to keep the names of variables used in the iterator and condition meaningful and easy to read, so that someone who reads it later (maybe even future you!) can more easily understand the logic.
for (int row = 1; row < 21; row += 2)
{
for (int num = 0; num < row; num ++)
{
Console.Write("*");
}
Console.WriteLine();
}
The first loop creates the uneven number of asteriks (1, 3, 5, 7, etc) to print the second loop will print them.
Understanding why'd you do something is very imported in programming Read this it may help, apart from that you could see the below solutions for your problem
// Loop through 1 to 7
for (int row = 1; row <= 7; row ++)
{
// print astrick only if you have a odd row
if(row%2 != 0)
{
for (int num = 0; num < row; num ++)
{
Console.Write("*");
}
Console.WriteLine();
}
// print empty line for even rows
else
{
Console.WriteLine();
}
}

Iterate over each value per row in DataTable and compare the value

I'm trying to read and then export a complicated excel sheet using c# and exceldatareader.
Basically, I want to grab specific rows e.g rows 10 to 50 and then iterate over each row and values within the row so that I can compare the values cell by cell.
E.g I grab the row 10 and it has a fixed number of columns so I need to iterate over each cell in this row, grab the first 4 and then after that if I find a a value other than 0 or null, I grab that value then go some fixed number of rows above that particular cell (where the value other than null or 0 was found) and grab another value.
Any help would be appreciated.
Here is what i have so far. I can iterate over the rows I need and then read the first 4 columns and then for the rest I need to only find the specific cells and then go some steps above that particular cell. What will be the most efficient way to solve this?
for (int i=15; i< 25; i++)
{
for( int j=1; j< 20; j++)
{
if(j < 4)
{
Console.WriteLine(dt2.Rows[i][j]);
}
else
{
//if you find any value other than 0 or null grab the position of this cell and then go to a specific cell in top most row.
}
}
}
It sounds like you just need to play with the indexes like so:
for (int i=15; i< 25; i++)
{
for( int j=1; j< 20; j++)
{
if(j < 4)
{
Console.WriteLine(dt2.Rows[i][j]);
}
else
{
if (dt2.Rows[i][j] does not equal 0 or null)
{
Console.WriteLine(dt2.Rows[i-some_fixed_#_of_rows][j]);
}
else
{
...
}
}
}
}
Of course, you also have to check if i-some_fixed_#_of_rows does not take you out of bounds.

Issue updating rows c#

I have a problem where I can either update by one row (and that's it) or by four at once.
The issue is with tbl.Rows.AddAt(tbl.Rows.Count - 1, tr1) on the last two lines, comments are indicating what happens when the statement is placed there.
There will be a limit of 6, but I can't figure out why I can't update one row at a time more than once with the code I've got.
The for loops are only allowing four cells per row and no more than 4 rows (not including the initial row at start).
Can you point me in the right direction please?
C#:
public void addRows_Click1(object sender, EventArgs e)
{
rmvRows.Visible = true;
// rows
for (int rowCount = 0; rowCount < 4; rowCount++ )
{
tr1 = new TableRow();
// cells
for (int cellCount = 0; cellCount < 4; cellCount++)
{
tc1 = new TableCell();
tb1 = new TextBox();
tb1.ID = "tbID" + cellCount.ToString();
tc1.Controls.Add(tb1);
tr1.Cells.Add(tc1);
}
tbl.Rows.AddAt(tbl.Rows.Count - 1, tr1);// will add four more rows
}
tbl.Rows.AddAt(tbl.Rows.Count - 1, tr1)// adds one one but no more
}
This line:
tbl.Rows.AddAt(tbl.Rows.Count - 1, tr1);// will add four more rows
is within a for loop that is running 4 times (from 0 to 3):
for (int rowCount = 0; rowCount < 4; rowCount++ )
{
tr1 = new TableRow();
/* code omitted */
tbl.Rows.AddAt(tbl.Rows.Count - 1, tr1);// will add four more rows
}
The line itself only adds one row each time it is called, but it is getting called 4 times. Therefore, the table has 4 additional rows after the for loop's execution.
As for your second line:
tbl.Rows.AddAt(tbl.Rows.Count - 1, tr1)// adds one one but no more
This is outside of any loop, so, like any other normal code, it only executes once. Since it only executes once, it only adds one row.
Additionally, since tr1 is not changed after the for loop, the final row added by that last line is going to be a duplicate of whatever the last row created by the loop was.
tbl.Rows.AddAt(tbl.Rows.Count - 1, tr1)
Only adds one row regardless were it is called because tr1 is just one row.
The AddAt will only allow one row to be added at a curtain position.
The AddAt used in the loop should work just fine.
Hope that helps!

Modulus usage when dealing with odd numbers

I have a list of roughly 50~60 items that I want to be able to divide into multiple columns dynamically. I'm using a nested for loop and the lists divide properly when there are an even number of items. However, when there are an odd number of items the remainder (modulus) items get left out. I've been playing around with it for a while and have not struck gold yet. I'm hoping someone smarter than me can & will assist.
Thanks.
for (int fillRow = 0; fillRow < numOfCols; fillRow++)
{
for (int fillCell = 0; fillCell < (siteTitles.Count / numOfCols); fillCell++)
{
linkAddress = new HyperLink();
linkAddress.Text = tempSites[fillCell].ToString();
linkAddress.NavigateUrl = tempUrls[fillCell].ToString();
mainTbl.Rows[fillCell].Cells[fillRow].Controls.Add(linkAddress);
}
}
Well yes, the problem is here:
fillCell < (siteTitles.Count / numOfCols)
That division will round down, so for example if there are 13 titles and numOfCols is 5, it will give 2 - which means that items 10-12 won't be used.
I suggest that actually you loop over all the items instead, and work out the row and column for each item:
for (int i = 0; i < siteTitles.Count; i++)
{
int row = i / numOfCols;
int col = i % numOfCols;
// Fill in things using row, col and i
}
(It's not exactly clear what you're doing as you're using siteTitles in the loop condition and tempSites in the loop body, and you're not using fillRow when extracting the data... basically I think you've still got some bugs...)

Categories

Resources