IndexOutOfRange Exception when working with DataTable

I'm using StreamReader to access to CSV text file and read all the lines to a DataTable.

public static DataTable ConvertToDataTable(string filePath, int numberOfColumns)
    {
        DataTable tbl = new DataTable();

        for (int col = 0; col < numberOfColumns; col++)
            tbl.Columns.Add(new DataColumn("Column" + (col + 1).ToString()));

        string line;
        System.IO.StreamReader file = new StreamReader("C:/ProgramData/3CX/Instance1/Data/Logs/CDRLogs/cdr.log");

        while ((line = file.ReadLine()) != null)
        {
            var cols = line.Split(',');
            DataRow dr = tbl.NewRow();
            for (int cIndex = 0; cIndex < cols.Length + 1; cIndex++)
            {
                dr[cIndex] = cols[cIndex];
            }
            tbl.Rows.Add(dr);
        }

Once I've added all the CSV rows to the DataTable I want to iterate through the rows and remomve unwanted rows on conditions.

DataTable dt = ConvertToDataTable("C:/ProgramData/3CX/Instance1/Data/Logs/CDRLogs", 4);

        for (int i = 0; i < dt.Rows.Count; i++)
        {
            string duration = dt.Rows[i][1].ToString();
            if (duration == "")
            {
                dt.Rows[i].Delete();
            }
            Console.WriteLine(i.ToString() + dt.Rows[i][1].ToString());
        }

This runs just fine until I reach the last row, where it seems to be looping through a row that doesn't exist.

Anyone have an idea as to why? Any and all help would be greatly appreciated!

Upvotes: 2

Views: 197

Answers (6)

Yasin
Yasin

Reputation: 1232

DataTable dt = ConvertToDataTable("C:/ProgramData/3CX/Instance1/Data/Logs/CDRLogs", 4);

    for (int i = 0; i < dt.Rows.Count; i++)
    {
        string duration = dt.Rows[i][1].ToString();
        if (duration == "")
        {
            dt.Rows[i].Delete();
            continue;                //Not printing deleted objects. So line after IF block won'nt exist.
        }
        Console.WriteLine(i.ToString() + dt.Rows[i][1].ToString());
    }

Upvotes: 0

Gian Paolo
Gian Paolo

Reputation: 4249

What I usually do when I have to delete elements from the collection I'm iterating is to save the rows delete in a different collection. Something like this, for your case:

int i = 0; // if you need to dump also the number of the row
List<DataRow> toDelete = new List<DataRow>();
foreach (var row in dt.Rows)
{
    string duration = row[1].ToString();
    if (duration == "")
    {
        toDelete.Add(row);
    }
    Console.WriteLine(i.ToString() + row[1].ToString());
    i++;
}
foreach (var rowToDelete in toDelete)
    row.Delete();

Upvotes: 1

Mostafiz
Mostafiz

Reputation: 7352

Increment i at the end of the loop

for (int i = 0; i < dt.Rows.Count; )
{
    string duration = dt.Rows[i][1].ToString();
    if (duration == "")
    {
        dt.Rows[i].Delete();
    }
    else
    {
        i++;
    }
    Console.WriteLine(i.ToString() + dt.Rows[i][1].ToString());
}

Upvotes: 1

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

When deleting, loop backward, instead of:

for (int i = 0; i < dt.Rows.Count; i++)
{
  ...    
  dt.Rows[i].Delete();
  ... 
}

Put

for (int i = dt.Rows.Count - 1; i >= 0 ; --i)
{
    string duration = dt.Rows[i][1].ToString();

    if (duration == "")
    {
        dt.Rows[i].Delete();
    }
    Console.WriteLine(i.ToString() + dt.Rows[i][1].ToString());
}

In case you have to loop forward for whatever reason, modify the loop into

for (int i = 0; i < dt.Rows.Count; ) // do not increment here
{
    string duration = dt.Rows[i][1].ToString();

    if (duration == "")
    {
        dt.Rows[i].Delete();
    }
    else
        i += 1; // ... but increment here 

    Console.WriteLine(i.ToString() + dt.Rows[i][1].ToString());
}

Upvotes: 3

Allmighty
Allmighty

Reputation: 1519

You can iterate trough the rows backwards to keep the indices as they are.

for(int i = dt.Rows.Count - 1; i >= 0; i--)
{
   dt.Rows[i].Delete();
}

Upvotes: 4

MikkaRin
MikkaRin

Reputation: 3084

When you deleting the row, you shouldn't set i=i+1 ,because amount or rows was decreased

        int i = 0; 
        while(i < dt.Rows.Count)
        {
            string duration = dt.Rows[i][1].ToString();
            if (duration == "")
            {
                dt.Rows[i].Delete();
            }
            else
            {i++;}
            Console.WriteLine(i.ToString() + dt.Rows[i][1].ToString());
        }

Upvotes: 2

Related Questions