Removing Item from List<DBTables> in C# [duplicate] - c#

This question already has answers here:
What is the best way to modify a list in a 'foreach' loop?
(11 answers)
Closed 9 years ago.
I am currently working on a C# WPF project. I have a list which uses a class to store multiple values. The class is called DBTables and has the following inside:
class DBTables
{
public string selDatabase { get; set; }
public string selTable { get; set; }
}
I am creating a new instance of the list with the following code
List<DBTables> tableArr = new List<DBTables>();
I am adding new items to the List without any problems but the problem I am having is when it comes to removing an item from the list.
A an item is added to the list when a checkbox is selected the item is added and when the checkbox is unchecked the item needs to be removed. Each time the checkbox is checked two values are added using the following code:
private void addBackupArray(string table)
{
backupArr.Add(new DBTables
{
selDatabase = selectedDatabase,
selTable = table
});
}
When the check box is unchecked the values at the position need to be removed and I have sort of got it working but after it has removed the item it then displays the error 'InvalidOperationException, collection was modified; enumeration may not execute'.
Below is the code that I am currently using to remove the item from the list.
private void removeBackupArray(string table)
{
int i = 0;
foreach (DBTables tables in backupArr)
{
if (selectedDatabase == tables.selDatabase && table == tables.selTable)
{
backupArr.RemoveAt(i);
i = 0;
}
i++;
}
}
The code above iterates through the values in the list and based on an if statement of whether the two variables match the value found in the list it removes it at the current position of the counter i.
How can I get round this issue so I can remove the item without getting the error.
Thanks for any help you can provide.

Change the foreach to normal for loop will fix the issue:
for (int tablesIndex = 0; tablesIndex < backupArr.Count; tablesIndex++)
{
var tables = backupArr[tablesIndex];
if (selectedDatabase == tables.selDatabase && table == tables.selTable)
{
backupArr.RemoveAt(tablesIndex);
tablesIndex--;
}
}

Changed your foreach to a for loop. The foreach uses an enumerator to iterate over all of the objects in the List. You can't change the contents of the enumerator within the foreach or you'll get the error you see.
Give this a try instead
int i;
for (i = 0; i < backupArr.Count; i++)
{
DBTables tables = backupArr[i];
if (selectedDatabase == tables.selDatabase && table == tables.selTable)
{
break;
}
}
backupArr.RemoveAt(i);

A neater solution could be to use a linq like so:
DBTables tables = backupArr.Where(t => t.selDatabase == selectedDatabase && t.selTable == table).SingleOrDefault();
if (tables != null)
backupArr.Remove(tables);

Related

Delete the current element in an array

How can I delete the current element of an array inside a foreach-loop?
My program gets data form a DB and sends it to a new one via HTTP requests. Now I want to post a JSON string to my new DB. If it was a success I want to delete the current array item which I'm working with. Something like this.
foreach(var item in array)
{
bool decide = method.DoSomething();
if(decide == true)
{
//delete current item
}
}
since you cannot delete items from an array and change the size of it here is a loop approach using a second collection
List<itemClass> keepCollection = new List<itemClass>();
foreach(var item in array)
{
bool decide = method.DoSomething();
if(decide == false)
{
keepCollection.Add(item);
}
}
If you need it again in array form just call ToArray()
var finalResult = keepCollection.ToArray();
appraoch with Linq which creates a new array with valid elements and overwrites the existing array
array = array.Where(x => !method.DoSomething(x)).ToArray(); //select valid elements
there are 2 ways (both were tested)
foreach (var item in array.ToList())
{
bool decide = method.DoSomething();
if (decide == true)
{
item.Remove();
}
}
and
for ( i=0; i < array.Length; i++)
{
bool decide = method.DoSomething();
if (decide == true)
{
array[i].Remove();
}
}
Whenever you want to delete entries from a collection, you should never loop through that collection from beginning to end, but always from end back to beginning.
By the way, C# does not allow you deleting entries from a collection while looping through that collection using a foreach loop.

Removing elements from a local data structure effects the parent object

I have the following code where I am checking if some elements are not matching in my dictionary then I want to remove the unmatching elements from the local item. The problem is, When a value is removed from the collection, for some reason it also modifies the parental structure.
My other problem is, for example if I have list as "A","B","B", using the Except is only giving me the single B but not the other. Please help.
public void AddLogs(IEnumerable<ReportGenerationTypes> subElements)
{
var changeDetails = new Dictionary<AuditSaveHeader, List<string>>();
List<string> AuditableItems = null;
List<string> subItems = new List<string>();
foreach (var item in subElements)
{
subItems.Add(item.ToString());
}
foreach (var item in auditLogData?.AuditHeaders)
{
if (!changeDetails.ContainsKey(item))
{
changeDetails.Add(item, null);
}
AuditableItems = new List<string>();
foreach (var inner in item.AuditChangeValues)
{
AuditableItems.Add(inner.Auditable.ToString());
}
changeDetails[item] = AuditableItems;
}
for (int i = 0; i < changeDetails.Count; i++)
{
var result = kp.Value.Except(subItems);
Auditable AuditItem = Auditable.Unassigned;
//I think the problem lies with the below code not sure.
if (result != null && result.Count() > 0)
{
foreach (var item in result)
{
Enum.TryParse(item, out AuditItem);
var itemToRemove = kp.Key.AuditChangeValues.Where(x => x.Auditable == AuditItem).FirstOrDefault();
//The following line effects the AuditChangeValues object and not just my dictionary.
kp.Key.AuditChangeValues.Remove(itemToRemove);
}
}
}
}
Promoting my comment to answer:
You are using some vars that are not shown, like kp, auditLogData, etc. and overall is not clear what you want to achieve.
Anyway I agree the problem is you are editing the reference to an object. You could try cloning the objects, etc. But without really understanding the code is hard to tell.

Remove Items from an ObservableCollection while Looping through the Collection [duplicate]

This question already has answers here:
RemoveAll for ObservableCollections?
(8 answers)
Closed 5 years ago.
I am looping through an ObservableCollection for items that have been marked by setting a boolean to true. After doing what I need with the item I want to remove it from the OnservableCollection. I can't do that within the foreach loop, so I was thinking of making a list of all the items marked, then after the foreach loop just clear all the items from the collection at once. But I can't even get started because I don't know how to get the index of the item in the foreach loop.
private void Upload()
{
List<TestResult> kill;
foreach (var tr in TestResults)
{
if (tr.CanUpload)
{
StatusContent = "Uploading " + tr.FileName;
FileOps.UploadDocument(tr);
kill.Add(tr);
tr.CanUpload = false;
RaisePropertyChanged("TestResults");
}
}
//this doesn't work
//TestResults.Remove(TestResults.Where(x => x.CanUpload == true));
}
I've tried working with what I have above, but I am missing some critical pieces to make it work right.
Use a for loop instead. Like you noticed you can not remove items in a collection you are looping with a foreach.
But you have to update your current item index when removing items ;)
Something like this should do the work
for (var i = myList.Count() - 1; i >= 0; i--)
{
...
}

c# How to prevent duplicate listview by column text

I need to prevent Duplicate entries from ListView controller by column text. if duplicate found I need to get the ListView Item for further process. I saw every one says
ListViewItem item = ListView3.FindItemWithText("test");
if (!listView1.Items.ContainsKey(txt))
{
// doesn't exist, add it
}
but how can I point which Column text?? I did prevent duplicates by adding ids into a array and after check array value exists. but in that case I can find which entry duplicated.
this is my code.
rd = cmd.ExecuteReader();
// Validation not working - duplicating ListviewItems
while (rd.Read()) {
ListViewItem lvvi = new ListViewItem(rd.GetString(0));
lvvi.SubItems.Add(rd.GetString(1));
lvvi.SubItems.Add(rd.GetString(5));
lvvi.SubItems.Add("1");
lvvi.SubItems.Add(rd.GetString(0));
int listViewItemID;
int[] ids;
ids = new int[100];
if (listView3.Items.Count > 0)
{
int addingItemID;
//ADD ListView ids into array
int i=0;
foreach (ListViewItem li in listView3.Items)
{
listViewItemID = Int32.Parse(li.SubItems[0].Text);
addingItemID = Int32.Parse(rd.GetString(0));
ids[i] = listViewItemID;
i++;
}
//Check item allready exsist
if (ids.Contains(Int32.Parse(rd.GetString(0))))
{
MessageBox.Show("sdsd");
}
else {
listView3.Items.Add(lvvi);
}
}
else {
listView3.Items.Add(lvvi);
}
}
//Calculate Price summery
this.calculatePrice();
Instead of looping to get all id's, you can loop through the items or use linq to find the specific id and keep the result. This can be done in an external function or by replacing the ids portion with the loop or use something like FirstOrDefault:
addingItemID = rd.GetString(0);
ListViewItem existing = listView3.Items.Cast<ListViewItem>().FirstOrDefault(li => li.SubItems[0].Text == addingItemID); //(not sure if the cast is needed)
if (existing != null)
{
//item exists, variable existing refers to the item
MessageBox.Show("sdsd");
}
else
{
listView3.Items.Add(lvvi);
}

Iterate over custom list and Remove item from list conditionally in C#

I want to iterate over a custom list i.e. defined as:
List<CurrentCluster> _curClusters = new List<CurrentCluster>();
IEnumerator<CurrentCluster> _clusIterate = _curClusters.GetEnumerator();
while (_clusIterate.MoveNext())
{
// Error_01: Cannot implicitly convert CurrentCluster to Cluster
Cluster _curClus = _clusIterate.Current; // Cluster is base class while
// CurrentCluster is derived class
// Error_02: Does not contain a definition for GetClusterSize()
if (_curClus.GetClusterSize() == 0)
{
// Error_03: Remove(char) has some invalid arguments.
_clusIterate.ToString().ToList().Remove(_curClus);
}
}
while method GetClusterSize() is defined in class Cluster.cs as:
public int GetClusterSize()
{
return _clusterObjects.Count;
// _clusterObjects is a defined in this class as:
// List<EvoObject> _clusterObjects = new List<EvoObject>();
}
If the size of specific cluster is equal to zero in that cluster list (i.e. _curClusters then to remove that cluster from the list.
How can we iterate over a custom list and remove item from list conditionally?
How about just using List RemoveAll method and doing this?
_curClusters.RemoveAll(_curClus=>_curClus.GetClusterSize() == 0);
You should be able to use a for loop - you have to work backwards because otherwise you would be moving the elements and some would get skipped.
for (int n=_curClusters.Count; n>=0; n--)
{
if (_curClusters[n].GetClusterSize()==0)
{
_curClusters.RemoveAt(n);
}
}
Removing items from a collection using iteration is both advanced and obsolete technique. Use LINQ instead:
_curClusters = _curClusters.Where(c => c.GetClusterSize() > 0).ToList();
Now curClusters contains just "sized clusters", whatever that means.
If you insist to do it through iterations this is the way:
The catch is that you MUST NOT change a collection while iterating over its items. Instead, you can iterate and determine if an item needs to be deleted and mark it somehow - for instance you can add it to another list which contains only items to be deleted. After the first iteration over the original collection, start second one and remove the items from the original, like so:
var toBeRemoved = new List<CurrentCluster>();
foreach (var suspiciousCluster in _curCluseters)
{
if(suspiciousCluster.GetClusterSize() == 0)
{
toBeRemoved.Add(suspiciousCluster);
}
}
foreach (var voidCluser in toBeRemoved)
{
_curCluster.Remove(voidCluster);
}
Again, _curClusters contains just "sized clusters", whatever this might mean.
However I highly recommend the first approach.
I did not understand why you are going with that complexity ... simply you can achieve the goal by below code
List<CurrentCluster> _curClusters = new List<CurrentCluster>();
_curClusters.RemoveAll(i => i.GetClusterSize()== 0);
//OR
for (int i = 0; i < _curClusters.Count; )
{
//If you have some more logical checking with CurrentCluster
//before remove
if (_curClusters[i].GetClusterSize()== 0)
{
_curClusters.Remove(_curClusters[i]);
continue;
}
i++;
}

Categories