I have listA, listB. listA is subset of listB. For example, 1 element is deleted and 2 elements are added to listB. Then if listA contains the element that was deleted from listB, delete it from listA. Also, listA should add the newly added elements.
At present I am using foreach{ if(list.contains) } two times. Once to add and once to delete. This will be of O(2n), which is ok.
But is there a best way to do this mostly with O(n) in LINQ/any other way?.
To be more clear:
Actually I have a list of custom class.
From which I am forming listA in above question(using one field of that). ListB is just list of string which I get from web service.
Code:
//First foreach loop which I was taking about.
foreach (string A in listA)
{
if (listB.Contains(A)
{
}
else
{
//getting items that are added to listB
}
}
//Second foreach loop which i was taking about.
foreach (string A in listB)
{
if (listA.Contains(A)
{
}
else
{
//getting items that are deleted from listB
}
}
And then I am updating that List<custom class> accordingly. My main question is instead of using two foreach loops can I do something better?
This might be more efficient (although that depends):
var notInA = listB.Except(listA).ToList();
var notInB = listA.Except(listB).ToList();
foreach (var a in notInA)
listA.Add(a);
foreach (var b in notInB)
listA.Remove(b);
Note that you need to implement a custom IEqualityComparer<T> if T is a custom class.
EDIT: so this is just synchronizing both lists. Maybe i've misunderstood the question but can't you simply:
listA = new List<T>(listB);
Can you use events / delegates instead of foreach ? Read a discussion here
You should be able to make these lists observable. When one list is updated, trigger the CollectionChanged event, and add in some code to update your other list. You should be able to make this two way. See Observable Collections : Here
Furthermore, observable collections allow you to detect which kind of events occured on your collection. (Ie. Add, Remove, Replace etc.) This should help you in your process of updating the other list with the same information.
Related
In the foreach loop, I want to add the Products to a List, but I want this List to not contain duplicate Products, currently I have two ideas solved.
1/ In the loop, before adding the Product to the List, I will check whether the Product already exists in the List, otherwise I will add it to the List.
foreach (var product in products)
{
// code logic
if(!listProduct.Any(x => x.Id == product.Id))
{
listProduct.Add(product);
}
}
2/. In the loop, I will add all the Products to the List even if there are duplicate products. Then outside of the loop, I would use Distinct to remove duplicate records.
foreach (var product in products)
{
// code logic
listProduct.Add(product);
}
listProduct = listProduct.Distinct().ToList();
I wonder in these two ways is the most effective way. Or have any other ideas to be able to add records to the List to avoid duplication ??
I'd go for a third approach: the HashSet. It has a constructor overload that accepts an IEnumerable. This constructor removes duplicates:
If the input collection contains duplicates, the set will contain one
of each unique element. No exception will be thrown.
Source: HashSet<T> Constructor
usage:
List<Product> myProducts = ...;
var setOfProducts = new HashSet<Product>(myProducts);
After removing duplicates there is no proper meaning of setOfProducts[4].
Therefore a HashSet is not a IList<Product>, but an ICollection<Product>, you can Count / Add / Remove, etc, everything you can do with a List. The only thing you can't do is fetch by index
You first take which elements are not already in the collection:
var newProducts = products.Where(x => !listProduct.Any(y => x.Id == y.Id));
And then just add them using AddRang
listProduct.AddRagne(newItems)
Or you can use foreach loop too
foreach (var product in newProducts)
{
listProduct.Add(product);
}
1 more easy solution could be there no need to use Distint
var newProductList = products.Union(listProduct).ToList();
But Union has not good performance.
From what you have included, you are storing everything in memory. If this is the case, or you are persisting only after you have it ready you can consider using BinarySearch:
https://msdn.microsoft.com/en-us/library/w4e7fxsh(v=vs.110).aspx and you also get an ordered list at the end. If ordering is not important, you can use HashSet, which is very fast, and meant specially for this purpose.
Check also: https://www.dotnetperls.com/hashset
This should be pretty fast and take care of any ordering:
// build a HashSet of your primary keys type (I'm assuming integers here) containing all your list elements' keys
var hashSet = new HashSet<int>(listProduct.Select(p => p.Id));
// add all items from the products list whose Id can be added to the hashSet (so it's not a duplicate)
listProduct.AddRange(products.Where(p => hashSet.Add(p.Id)));
What you might want to consider doing instead, though, is implementing IEquatable<Product> and overriding GetHashCode() on your Product type which would make the above code a little easier and put the equality checks where they should be (inside the respective type):
var hashSet = new HashSet<int>(listProduct);
listProduct.AddRange(products.Where(hashSet.Add));
This seems like it should be answered but potential dupes I found were asking different things...
I noticed that this seems to work fine (sourceDirInclusion is a simple Dictionary<X,Y>)
foreach (string dir in sourceDirInclusion.Keys)
{
if (sourceDirInclusion[dir] == null)
sourceDirInclusion.Remove(dir);
}
Does that mean removing items from a collection in foreach is safe, or that I got lucky?
What about if I was adding more elements to the dictionary rather than removing?
The problem I'm trying to solve is that sourceDirInclusion is initially populated, but then each value can contribute new items to the dictionary in a second pass. e.g what I want to do is like:
foreach (string dir in sourceDirInclusion.Keys)
{
X x = sourceDirInclusion[dir];
sourceDirInclusion.Add(X.dir,X.val);
}
Short answer: This is not safe.
Long answer: From the IEnumerator<T> documentation:
An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.
Note that the docs say the behavior is undefined, which means that it might work and it might not. One should never rely on undefined behavior.
In this case, it depends on the behavior of the Keys enumerable, regarding whether or not it creates a copy of the list of keys when you begin enumerating. In this specific case, we know from the docs that the return value from Dictionary<,>.Keys is a collection that refers back to the dictionary:
The returned Dictionary<TKey, TValue>.KeyCollection is not a static copy; instead, the Dictionary<TKey, TValue>.KeyCollection refers back to the keys in the original Dictionary<TKey, TValue>. Therefore, changes to the Dictionary<TKey, TValue> continue to be reflected in the Dictionary<TKey, TValue>.KeyCollection.
So it should be considered unsafe to modify the dictionary while enumerating the dictionary's keys.
You can correct this with one change. Alter this line:
foreach (string dir in sourceDirInclusion.Keys)
To this:
foreach (string dir in sourceDirInclusion.Keys.ToList())
The ToList() extension method will create an explicit copy of the list of keys, making it safe to modify the dictionary; the "underlying collection" will be the copy and not the original.
If will throw
InvalidOperationException: Message="Collection was modified; enumeration operation may not execute
To avoid that add candidates for removal to an external list. Then loop over it and remove from target container (dictionary).
List<string> list = new List<string>(sourceDirInclusion.Keys.Count);
foreach (string dir in sourceDirInclusion.Keys)
{
if (sourceDirInclusion[dir] == null)
list.Add(dir);
}
foreach (string dir in list)
{
sourceDirInclusion.Remove(dir);
}
check this out: What is the best way to modify a list in a 'foreach' loop?
In short:
The collection used in foreach is immutable. This is very much by design.
As it says on MSDN:
The foreach statement is used to iterate through the collection to get the information that you want, but can not be used to add or remove items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop.
UPDATE:
You can use a for loop instead:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
This works because you are traversing sourceDirInclusion.Keys.
However, just to be sure with future versions of the FrameWork I recommend that you use sourceDirInclusion.Keys.ToArray() in the foreach statement this way you will create a copy of the keys that you loop through.
This will however not work:
foreach(KeyValuePair<string, object> item in sourceDirInclusion)
{
if (item.Value == null)
sourceDirInclusion.Remove(item.Key);
}
As a rule, you cannot modify a collection while it is traversed, but often you can make a new collection by using .ToArray() or .ToList() and traverse that while modifying the original collection.
Good luck with your quest.
Here is my code:
foreach (DataGridViewRow r in dgv01.Rows)
if (r.Cells[0].Value.ToString() == abc)
{
dgv01.Rows.Remove(r);
//dgv01.CurrentCell = dgv01.Rows[0].Cells[0]; - **also tried**
}
But only some rows are deleted - not all specified !?
Why - foreach - does not mean - foreach ?
I have to remind you that it is dangerous to use a foreach block when you want to modify/remove the data being traversed. The removal messes up with the index of the foreach iterator.
Solution? Use a reverse for loop instead.
First of all it is not a good idea to change collection inside foreach loop. Secondly foreach is not behaving how you want because once the first row is removed then second row will become the first row but because in foreach the checking will be done on second row which is actually a third row. so, checking on the second row has been skipped. The best way to do is use for loop in a reverse order.
to know why foreach is not behaving like foreach try this simple example.
private List<int> list = Enumerable.Range(0, 10).ToList<int>();
for(int i=0;i<list.Count;++i)
{
if (list[i] < 5)
list.RemoveAt(i);
}
list.ForEach(x => Console.Write(x));
Output:
1356789
When you iterate over a collection using foreach, the collection should not be modified otherwise, in the best of scenarios, you end up not covering the entire collection. Most of the time, however, modifying the collection while within a foreach block, the iteration will fail.
In order to remove the rows from the DataGridView, you'll have to use a for iteration block or a two-step process both of which I display below.
for Iteration Block
This procedure uses an index to traverse the collection, modifying the collection as you go. The thing you need to know here is that if you intend to modify the collection, you do not have the index increment (go forward through the collection); you decrement the index (go backwards through the collection). If you modify (remove or add items) the collection while iterating forwards, you may not end up visiting all the items in the collection.
Here is code to remove rows from the DataGridView. Remember, we iterate backwards.
for (int i = dgv01.Rows.Count - 1; i >= 0; i--)
{
if (dgv01.Rows[i].Cells[0].Value.ToString() == "abc")
{
dgv01.Rows.RemoveAt(i);
}
}
foreach Iteration Block
This procedure uses a two-step approach. The first step finds the items to be removed and the second step removes the items from the collection. This two-step process is necessary for the reasons I explained earlier.
Now the code. Remember, it is a two-step process.
// step 1. find the items to be removed
//items to be removed will be added to this list
var itemsToRemove = new List<DataGridViewRow>();
foreach (DataGridViewRow r in dgv01.Rows)
{
if (r.Cells[0].Value.ToString() == "abc")
{
itemsToRemove.Add(r);
}
}
//step 2. remove the items from the DataGridView
foreach (var r in itemsToRemove)
{
// this works because we're not iterating over the DataGridView.
dgv01.Rows.Remove(r);
}
One last thing you should know is that the procedures I have demonstrated here do not apply to the DataGridViewRowCollection class only; it applies to all collections that implement the IList<T> or the ICollection<T> interface. So, yes, the process can be used for Lists, Dictionaries, ControlCollections and all other similar classes.
do a descending sorting on column[0] then from bottom u can delete the rows. Once abc is over u can break the loop also.
Other wise u can put a row filter on dataTable u are using.
((DataTable)this.datagrid1.DataSource).DefaultView .RowFilter
Whenever I have to modify a collection inside a foreach I usually make a new collection which I fill with the data I want to modify, and then loop through this second collection to do the actual changes to the main collection. In this way I never modify a collection while looping it, so I avoid the situation described by MChicago.
In your case I usually would write this kind of code:
List<DataGridViewRow> toDel = new List<DataGridViewRow>();
foreach (DataGridViewRow r in dgv01.Rows)
if (r.Cells[0].Value.ToString() == "abc")
toDel.Add(r);
foreach (DataGridViewRow aRow in toDel)
dgv01.Rows.Remove(aRow);
What I am trying to do is to implement a heuristic approach to NP complete problem: I have a list of objects (matches) each has a double score. I am taking the first element in the list sorted by the score desc and then remove it from the list. Then all elements bound to the first one are to be removed. I iterate through the list till I have no more elements.
I need a data structure which can efficiently solve this problem, so basically it should ahve the following properties:
1. Generic
2. Is always sorted
3. Has a fast key access
Right now SortedSet<T> looks like the best fit.
The question is: is it the most optimal choice for in my case?
List result = new List();
while (sortedItems.Any())
{
var first = sortedItems.First();
result.Add(first);
sortedItems.Remove(first);
foreach (var dependentFirst in first.DependentElements)
{
sortedItems.Remove(dependentFirst);
}
}
What I need is something like sorted hash table.
I assume you're not just wanting to clear the list, but you want to do something with each item as it's removed.
var toDelete = new HashSet<T>();
foreach (var item in sortedItems)
{
if (!toDelete.Contains(item))
{
toDelete.Add(item);
// do something with item here
}
foreach (var dependentFirst in item.DependentElements)
{
if (!toDelete.Contains(item))
{
toDelete.Add(dependentFirst);
// do something with item here
}
}
}
sortedItems.RemoveAll(i => toDelete.Contains(i));
I think you should use two data structures - a heap and a set - heap for keeping the sorted items, set for keeping the removed items. Fill the heap with the items, then remove the top one, and add it and all its dependents to the set. Remove the second one - if it's already in the set, ignore it and move to the third, otherwise add it and its dependents to the set.
Each time you add an item to the set, also do whatever it is you plan to do with the items.
The complexity here is O(NlogN), you won't get any better than this, as you have to sort the list of items anyway. If you want to get better performance, you can add a 'Removed' boolean to each item, and set it to true instead of using a set to keep track of the removed items. I don't know if this is applicable to you.
If im not mistake, you want something like this
var dictionary = new Dictionary<string, int>();
dictionary.Add("car", 2);
dictionary.Add("apple", 1);
dictionary.Add("zebra", 0);
dictionary.Add("mouse", 5);
dictionary.Add("year", 3);
dictionary = dictionary.OrderBy(o => o.Key).ToDictionary(o => o.Key, o => o.Value);
For now, the best I could think of is:
bool oneMoreTime = true;
while (oneMoreTime)
{
ItemType toDelete=null;
oneMoreTime=false;
foreach (ItemType item in collection)
{
if (ShouldBeDeleted(item))
{
toDelete=item;
break;
}
}
if (toDelete!=null)
{
collection.Remove(toDelete);
oneMoreTime=true;
}
}
I know that I have at least one extra variable here, but I included it to improve the readability of the algorithm.
The "RemoveAll" method is best.
Another common technique is:
var itemsToBeDeleted = collection.Where(i=>ShouldBeDeleted(i)).ToList();
foreach(var itemToBeDeleted in itemsToBeDeleted)
collection.Remove(itemToBeDeleted);
Another common technique is to use a "for" loop, but make sure you go backwards:
for (int i = collection.Count - 1; i >= 0; --i)
if (ShouldBeDeleted(collection[i]))
collection.RemoveAt(i);
Another common technique is to add the items that are not being removed to a new collection:
var newCollection = new List<whatever>();
foreach(var item in collection.Where(i=>!ShouldBeDeleted(i))
newCollection.Add(item);
And now you have two collections. A technique I particularly like if you want to end up with two collections is to use immutable data structures. With an immutable data structure, "removing" an item does not change the data structure; it gives you back a new data structure (that re-uses bits from the old one, if possible) that does not have the item you removed. With immutable data structures you are not modifying the thing you're iterating over, so there's no problem:
var newCollection = oldCollection;
foreach(var item in oldCollection.Where(i=>ShouldBeDeleted(i))
newCollection = newCollection.Remove(item);
or
var newCollection = ImmutableCollection<whatever>.Empty;
foreach(var item in oldCollection.Where(i=>!ShouldBeDeleted(i))
newCollection = newCollection.Add(item);
And when you're done, you have two collections. The new one has the items removed, the old one is the same as it ever was.
Just as I finished typing I remembered that there is lambda-way to do it.
collection.RemoveAll(i=>ShouldBeDeleted(i));
Better way?
A forward variation on the backward for loop:
for (int i = 0; i < collection.Count; )
if (ShouldBeDeleted(collection[i]))
collection.RemoveAt(i)
else
i++;
You cannot delete from a collection inside a foreach loop (unless it is a very special collection having a special enumerator). The BCL collections will throw exceptions if the collection is modified while it is being enumerated.
You could use a for loop to delete individual elements and adjust the index accordingly. However, doing that can be error prone. Depending on the implementation of the underlying collection it may also be expensive to delete individual elements. For instance deleting the first element of a List<T> will copy all the remaning elements in the list.
The best solution is often to create a new collection based on the old:
var newCollection = collection.Where(item => !ShouldBeDeleted(item)).ToList();
Use ToList() or ToArray() to create the new collection or initialize your specific collection type from the IEnumerable returned by the Where() clause.
The lambda way is good. You could also use a regular for loop, you can iterate lists that a for loop uses within the loop itself, unlike a foreach loop.
for (int i = collection.Count-1; i >= 0; i--)
{
if(ShouldBeDeleted(collection[i])
collection.RemoveAt(i);
}
I am assuming that collection is an arraylist here, the code might be a bit different if you are using a different data structure.