2

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!

3
  • 2
    Where exactly you got exception? Maybe last row does not contain 2nd column? dt.Rows[i][1] Commented Aug 12, 2016 at 9:28
  • As a rule of thumb, never delete an element of a collection you are iterating, Even with a for cycle, you need to pay some attentions (such iterating backwards, as @Adimeus suggests). In your code, if you delete the last row, next line you try to access to Rows[i], which does not exists any more. and even if it's not the last row, Rows[i] refers to the row following the one you deleted. Not what you want to write on console, I assume. Commented Aug 12, 2016 at 9:36
  • < cols.Length + 1 also seems fishy, no? Commented Aug 12, 2016 at 10:18

6 Answers 6

4

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();
}
Sign up to request clarification or add additional context in comments.

Comments

3

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());
}

Comments

2

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());
        }

Comments

1

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());
}

Comments

1

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();

Comments

0

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());
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.