How to properly update a datatable while inside a loop - c#

In my below code I'm comparing values (email addresses) in my Arrarylist (called emails) with values in my Datatable (called dtResult). I was hoping to be able to remove rows from the datatable if it has an email address that is found in the arraylist but I'm starting to think I'm better off creating a new datatable that does not have those rows instead (I just don't know how to do that). Obviously my code below bombs after a row is deleted because it loses its place in the foreach loop. How can I fix this?
Thanks.
foreach (DataRow row in dtResult.Rows)
{
var tmpl = row[3];
for (int x = 0; x < emails.Count; x++) //emails is an ArrayList of email addresses
{
string one = emails[x].ToString().ToUpper();
string two = tmpl.ToString().ToUpper();
//compare datatable value to arraylist value..
if (one == two)
{
//if they are equal then I want to remove them (or maybe create a new datatable??)
dtResult.Rows.Remove(row);
dtResult.AcceptChanges();
}
}
}

The easiest way would be to loop through the DataTable backward:
for (int i = dtResult.Rows.Count - 1; i >= 0; i--)
{
var row = dtResult.Rows[i];
// ...
}
You also might want to consider whether you can just mark the row state on those rows as deleted (and then observe that status later) instead of actually removing them from the table.

You could go through the DataTable in reverse
for(int i = dtResult.Rows.Count; i >= 0; i--)
{
foreach(object email in emails)
{
if(email.ToString().ToUpper() == dtResult[i].ToString().ToUpper())
{
dtResult.Rows.Remove(dtResult.Rows[i]);
dtResult.AcceptChanges();
}
}
}

Why don't you do it in two steps?
I didn't try this, so let me know how it goes.
var deleteRows = new List<DataRow>();
then in your loop, instead of deleting the row right away, just add the row to your list:
deleteRows.Add(row);
and after you loop through your database, you loop through the deleteRows list and delete each row one by one.

Related

Count the frequency of each elements in a DataGridView in .Net Windows Application Form

I have some datasets that I read from csv files into DataGridViews and I need to plot some charts where I count the frequency of each different variable on each column.
I found a code that count the frequency of array elements and then print them, I tried using the same logic on my datagrid.
I started with a column ("SEX" column) that has only 2 values (2.0 or 1.0), but when I try to view the count result I always get 1 (which means it didn't count a thing). It's supposed to show the count of the last different value found in the column (in my case 2.0 which has 140ish occurence).
Edit: I tried appending the count result to the textbox and I see that I end up with 1 for each loop, while I am supposed to get only 2 values (which are the count 2.0and then the count of 1.0)
I also need to plot the output, so I am guessing I could use a dictionary where I store the variable name + the frequency.
public void countFreq() //function to count the frequency of each var in a column -not working yet-
{
var n = dataGridView1.RowCount;
var visited = new bool[n];
// Traverse through array elements and
// count frequencies
for (var i = 0; i < n; i++)
{
// Skip this element if already processed
if (visited[i] || dataGridView1.Rows[i].Cells["SEX"].Value
== null)
continue;
// Count frequency
var count = 1;
for (var j = i + 1; j < n; j++)
if (dataGridView1.Rows[i].Cells["SEX"].Value == dataGridView1.Rows[j].Cells["SEX"].Value)
{
visited[j] = true;
count++;
}
textFile.Text += count.ToString(); //for testing purposes I used a textfield to print the last count value
}
}
I know that for a column where the values are explicit I can just loop on my datagrid rows and use the count method (which I did) but for most my data I don't explicitly know the values in each row so I needed to find a way to do that.
I am not sure if this is what you are looking for. In addition, the code below loops through the rows in the grid, however, if the grid has a data source I suggest looping though that collection instead of the grid rows.
Below is a method that takes a column index and returns a Dictionary<string, int>. A simple loop through each cell in the given column and if the cell's Value is not in the dictionary, we will add it. If the cells value is already in the dictionary will simply increment its int Value. After the loop finishes, the dictionary is returned. Something like…
private Dictionary<string, int> GetCountOfValues(string columnName) {
string curKey = "";
Dictionary<string, int> valuesAndCounts = new Dictionary<string, int>();
foreach (DataGridViewRow row in dataGridView1.Rows) {
if (!row.IsNewRow) {
if (row.Cells[columnName].Value != null) {
curKey = row.Cells[columnName].Value.ToString();
if (valuesAndCounts.ContainsKey(curKey)) {
valuesAndCounts[curKey]++;
}
else {
valuesAndCounts.Add(curKey, 1);
}
}
}
}
return valuesAndCounts;
}
Usage may look something like…
Dictionary<string, int> column0Counts = GetCountOfValues("Col0");
Dictionary<string, int> column1Counts = GetCountOfValues("Date");
You should really load your CSV data into a datatable and then query that
var dt = SomeFunctionThatReadsCsvIntoDatatable(..);
yourDataGridView.DataSource = dt;
Your query is then answered, simply, by grouping the datatable using linq;
(yourDataGridView.DataSource as DataTable).Rows
.Cast<DataRow>()
.GroupBy(r => r["someColumn"])
.Select(g => new { K = g.Key, C = g.Count() });
"someColumn" would be "SEX"
K would end up as an object holding whatever Type the data is ` - it's hard to tell from the information posted whether you've just got your csv as strings or whether they're eg doubles, dates etc
If you want to do it for all columns, it would probably be easiest to do it in a loop on the datatable Columns collection. DataColumn.ColumnName provides the "someColumn"

Rows error help in listview function

Here is the code I am using.
foreach (DataRow row in data.Rows)
{
ListViewItem lst = default(ListViewItem);
lst = lvw.Items.Add(row(0)); // My error is on row
for (int i = 1; i <= data.Columns.Count - 1; i++)
{
lst.SubItems.Add(row(i));
}
}
Now, what I'm doing or trying to do is actually add database items onto my listview. However, I can't seem to get my rows onto my list view. I get a syntax error:
'Rows' is a 'variable' but is used like a 'method'.
I have been struggling with this winform for a couple days and my coding has stopped dead at this point. I have tried changing the parenthesis. Here is the whole function. I got it from the internet in vb.net and ran the code it worked. I then configured that code for my application in vb.net that worked too, now after converting to c# and it doesn't work which is my problem. I went through all the code(I am new to c# like relatively) and fixed most of it to work for my application except the row error
private void ShowDataInLvw(DataTable data, ListView lvw)
{
lvw.View = View.Details;
lvw.GridLines = true;
lvw.Columns.Clear();
lvw.Items.Clear();
foreach (DataColumn col in data.Columns)
{
lvw.Columns.Add(col.ToString());
}
foreach (DataRow row in data.Rows)
{
ListViewItem lst = default(ListViewItem);
lst = lvw.Items.Add(row(0));
for (int i = 1; i <= data.Columns.Count - 1; i++)
{
lst.SubItems.Add(row(i));
}
}
}
In C#, to get to an index of a collection, you need to use [ ] (whereas VB uses ( )). Refer to this specifically for DataRows.
Thus, your code should look like:
lst = lvw.Items.Add(row[0]);
And
lst.SubItems.Add(row[i])
On a side note, you may want to read up on the use of the default keyword.
EDIT:
Here's a complete listing (without spoon-feeding much), after noticing that your code won't actually work and assuming you really intend to do as described:
foreach (DataRow row in data.Rows)
{
ListViewItem lst = default(ListViewItem);
for (int i = 0; i <= data.Columns.Count - 1; i++)
{
if (i == 0)
lst = new ListViewItem(row[0].ToString());
else
lst.SubItems.Add(row[i].ToString());
}
lvw.Items.Add(lst);
}
Notice a few things:
The instantiation of lst has been changed.
The lvw will add the lst along with its subitems.
The addition will occur only after the subitems have been created.
Disclaimer: I haven't tested the code, but the idea is there.
Have you tried:?
lst = lvw.Items.Add(row[0]);
lst.SubItems.Add(row[I]);

Trying to transfer from one dataGridView to another accordingly

My datagridView1 contains 7 columns, and first one has dates.
I'm trying to transfer january data from dataGridView1 to dataGridView2
I did try using a loop, which logically thought it would work, but it would give me nullExceptions
Here's my code:
for (int i = 0; i < f1.dataGrivView1.Rows.Count; i++)
{
if (f1.dataGrivView1.Rows[i].Cells[0].Value.ToString().Split('/')[1].StartsWith("01"))//if january..
{
dataGrivView1.Rows.Add();//datagridview in current form
dataGrivView1.Rows[i].Cells[0].Value = f1.dataGrivView1.Rows[i].Cells[0].Value.ToString();
dataGrivView1.Rows[i].Cells[1].Value = f1.dataGrivView1.Rows[i].Cells[1].Value.ToString();
}}
And after some googling, I saw you can't just copy data this way and you need to clone it, so I tried.
foreach (DataGridViewRow dgvr in f1.dataGridView1.Rows)
{
DataGridViewRow r = dgvr.Clone() as DataGridViewRow;
foreach (DataGridViewCell cell in dgvr.Cells)
{
if (cell.Value.ToString().Contains("/01/"));
r.Cells[cell.ColumnIndex].Value = cell.Value;
}
dataGridView1.Rows.Add(r);
}
But.. same thing.
Any idea?
This does indeed copy the first column values from a DataGridView to another.
for(int i=0; i<f1.dataGridView1.Rows.Count; i++)
dataGridView1.Rows[i].Cells[0].Value = f1.dataGridView1.Rows[i].Cells[0].Value;
Any exceptions thrown are probably due to the new dataGridView1 not having the existing rows at the corresponding index in the iteration.

move datagridview Row to another datagridView

I've gone through the other answers for this kind of question, nothing has really worked well so far.
I have a foreach loop that should add the the row from the source datagridview to the destination datagridview then remove that row from the source.
The invalid operation exception is: Row provided already belongs to a DataGridView control.
I couldn't get ...Rows.Copy() to work either. Any ideas?
foreach (DataGridViewRow selRow in fromDataGridView.SelectedRows)
{
toDataGridView.Rows.Add(selRow);
fromDataGridView.Rows.Remove(selRow);
}
You need to remove the row from fromDataGridView before you add it to toDataGridView.
But you're modifying the collection inside the foreach loop - that won't work.
The workaround is to copy the collection for use in the foreach.
Try this:
foreach (DataGridViewRow selRow in fromDataGridView.SelectedRows.OfType<DataGridViewRow>().ToArray())
{
fromDataGridView.Rows.Remove(selRow);
toDataGridView.Rows.Add(selRow);
}
Here is an example of what you could do or try..
its happening when one row is removed the rows count decrements too so if you put your code in for loop and run it in reverse it would work fine have a look:
for (int selRow = dataGridView1.Rows.Count -1; selRow >= 0 ; selRow--)
{
toDataGridView.Rows.Add(selRow);
fromDataGridView.Rows.Remove(selRow);
}
Adding a new row directly to DataGridView is not possible when there is BindingSource.
You should add row to the BindingSource of second view and let it to add the row to its grid.
I've tested the below code in a working solution.
var selectedRows = dataGridView1.SelectedRows;
int count = dataGridView1.SelectedRows.Count;
for (int i = 0; i < count; i++)
{
bindingSource2.Add(bindingSource1[selectedRows[i].Index]);
dataGridView1.Rows.Remove(selectedRows[i]);
}

Remove a row from a DataTable within a DataSet

I have a DataSet with several DataTables inside. I'm displaying the DataTables in a ListView (I didn't know about databinding when I wrote the code). Anyway, I would like to remove rows from the DataTables inside the DataSet.
I have tried this:
foreach (DataRow row in dsData.Tables["Table1"].Rows)
{
//find the row that contains the username I'm after
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}
I've also tried
dsData.Tables["Table1".Rows.Delete(row);
The problem I'm experiencing is that the when you remove a row I get the exception:
Collection was modified; enumeration operation might not execute.
From what I understand it's because when you remove a row from a ListView the row below it moves up and causes all this trouble. The code itself does what it's supposed to but it's not nice to see that exception when when you run it.
I was about to rewrite the whole class with a DataGridView but would rather correct that single line if possible :).
EDIT: I'm not even sure a DataGridView would solve the problem anyway.
change the loop to a for loop counting backwards so you don't get that message.
for(int i = dsData.Tables["TAble1"].Rows; i > 0; i--)
{
if(item.SubItems[2].Text == dsData.Tables["Table1"].Rows[i - 1]["LoginName"].ToString())
dsData.Tables["Table1"].Rows.Remove(i - 1)
}
Try:
DataSet dsData = new DataSet();
List<DataRow> rowsToDelete = new List<DataRow>();
foreach (DataRow row in dsData.Tables["Table1"].Rows)
{
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
rowsToDelete.Add(row);
}
}
foreach(DataRow row in rowsToDelete)
{
dsData.Tables["Table1"].Rows.Remove(row);
}
You need a backwards for loop if you're going to be removing things
(explanation of why here)
for (int i = dsData.Tables["Table1"].Rows.Count - 1; i >= 0; i--)
{
DataRow row = dsData.Tables["Table1"].Rows[i];
//find the row that contains the username I'm after
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}
in general you can't remove items from a collection within a loop that is iterating on it
what you could do is keep a list of all the row you want to remove (creating it within the loop) and remove all of them OUTSIDE the loop
You can't modify a collection that you are iterating though with a foreach loop, from inside the loop. Do this instead:
for (int i = 0; i < dsData.Tables["Table1"].Rows.Count; i++)
{
DataRow row = dsData.Tables["Table1"].Rows[i];
if (item.SubItems[2].Text == row["LoginName"].ToString())
{
dsData.Tables["Table1"].Rows.Remove(row); //<- main code of interest
}
}

Categories

Resources