Foreach loop skip existing item - c#

Here is my code:
int k = panel.Controls.OfType<DataGridView>().Count<DataGridView>();
foreach (Control control in panel.Controls.OfType<DataGridView>())
{
panel.Controls.Remove(control);
}
I have 4 DataGridView objects on panel that are created at runtime with the names 0, 1, 2, and 3, and "k" its shown correct value(4). But my foreach loop's first step is "0", second is "2", and then the loop ends. It skips two object and I don't know why.
If I put a second and third foreach statement, the result is correct.
Why is that so?

In the first loop your Control is Zero.
You remove the control.
Your next loop gets the next Control. Since its already returned the first control, it now returns the second Control, but since you've removed the Zero control the second Control is now 2.
The key to understanding this behaviour is that the OfType() returns an iterator, not a list. If you returned OfType().ToList() you would get a concrete list that would not be changed when you alter the list you derived it from.
So;
IList<object> x = underlyingList.OfType<object>() returns an iterator.
List<object> y = underlyingList.OfType<object>().ToList() returns a concrete list.

When you delete objects in the list you're iterating over you also alter its length.
First iteration you're at index 0 and length 4.
Second iteration is at index 1 (originally this item was index 2) and length 3.
Then your loop will terminate because you're at index 2 but there's no element at that index anymore because the ones who were there are now at index 0 and 1.
If you want to remove all elements this way you can iterate over the list backwards instead, this way you won't offset the list and making you miss the elements.

Related

Insert into ObservableCollection per int comparison

Say I have an ObservableCollection with two items:
0: dateUnix: 333
1: dateUnix: 222
Now I want to add a new Item:
dateUnix: 300
If I just were to use the .add() method, the item would get added at the end. But I want the item to be inserted between 222 and 300 since this would make the list sorted.
How do I insert an item at a certain position where it is less then item value after and higher then item value before?
Of the top, I can think of two ways of doing this.
One would be, as was pointed out in the comments, to just insert and sort afterwards.
Another, more complex and more rewarding way would be to find the index of the first item greater or lesser than the one you're inserting and insert it at that index. Your list seems to be sorted in descending order, so it'd need to be the first lesser than.
You could achieve this using LINQ:
ObservableCollection<Int> collection = new ObservableCollection(new List<int>{333,222}); // == [333,222]
Int toInsert = 300;
collection.Insert(collection.IndexOf(collection.First(elem => elem < toInsert)), toInsert); // output == [333,300,222]
See this Fiddle for a working example.
If your collection is already sorted, just find the appropriate index to insert the element at (either via a linear or the faster binary search) and use Insert to store the element at that specific index.

Most efficent method for getting 10th item from a singlurary linked list

What is the most efficient method for getting the 10th item form the end of a list
I was thinking something like:
List[List.Count() - 10];
If you're using List<T> from System.Collections.Generic then you're not actually using singly linked list. It's backed by an array, and you can simple access it by index, as you already suggested:
list[list.Count - 10];
it will be O(1) operation. You should probably check if list has at least 10 elements before doing it so you don't get an exception.
However, if you have your own singly linked list structure you'll have to iterate the entire list to get Nth item from the end of the list. You can use the same approach, but that will force two round trips over the collection - first to get total number of elements and second to get Nth last element.
You can make it happen with just one iteration, if you store last N items you've seen, e.g. in a queue. This will be O(n) operation.

Removes all items from a System.Collections.ObjectModel.Collection in C#

I have the following collection where I enter some values using a method:
Collection<double> temp = new Collection<double>();
I would like to remove all the items from the collection at some point so I could be able to start filling it again from the beginning as I do the first time I initialize the program.
I tried like this but it doesn't work.
for (int i = 0; i < temp.Count; i++)
{
temp.RemoveAt(i);
}
What is wrong?
Thanks for your help!
Use the Clear method:
temp.Clear();
The reason why your loop doesn't work is because when you do a RemoveAt, the indexes of all items shift. So, say you started with this:
6, 2, 8, 5, 3
In the first iteration, i is 0, and the first item will be removed:
2, 8, 5, 3
For the second iteration, i is 1, but that's no-longer the next item to delete, so you get this:
2, 5, 3
Then, i is 2, and you get this:
2, 5
Now i is > temp.Count. See how you can be left with leftover items?
You can use Clear() method -
temp.Clear();
By calling RemoveAt, you are assuming there does exist an item at the given index. However, by removing it, the collection has one less item, and hence your for loop doesn't make sense. It won't throw an exception just because of temp.Count being evaluated during each iteration. In effect, it will remove only every other item from the collection.
Instead, use the Clear method.
You can't use indexed removal, as soon as you remove the first item, the collection gets reindexed, the second element becomes first, third becomes second etc. You will remove every odd element and get and exception after going out of the collection size. In .net implementation you would probably get an exception eariler, as soon as you remove the first item, the enumerator will complain that you can't modify the collection you are enumerating at the very moment.
There are few ways to work this around, the easiest it to clear the collection using dedicated method (Clear). In case of collection of references, you could also create a collection of copies of references from the original collection and iterate over the cloned collection but remove from the original.
If you really need to remove one element at a time (rather than calling Clear() to remove them all in one go) then you need to iterate in reverse order:
int count = temp.Count;
for (int i = count-1; i >= 0; i--)
{
temp.RemoveAt(i);
}
This will remove the elements from the end of the list so that your index is always within range of the remaining list and you remove all the items from the list.
Or only use the size as a counter:
int count = temp.Count;
for (int i = 0; i < count ; i++)
{
temp.RemoveAt(0);
}
This isn't using the collections iterators at all and as you're getting the count before you start removing elements it shouldn't fail.
Something like this would only be needed if you needed to do some processing on each element etc. before removing it from the list.

Find solution for an algorithm

I have a List<> in c# contains number of objects, for example 100. I need to select some objects in a row without repetition. For example, I select object 4 and then randomly a random number of sequential objects after that (object 5 and 6). When I want to select another collection, it shouldn't contains object 4, 5, and 6. If I remove these objects from List then sometimes I will receive object 2,3,7,8 that useless. In another words, I need some sub lists from main list without repeated objects and with the same order as main list. I was wondering if anybody help me to solve this algorithm.
You can store your objects in a Dictionary so you can keep an extra information for each object, in your case a boolean that tells you if your object has been selected or not, something like that :
//initializing the dictionary, any item has been selecte
Dictionary<object,bool> dic = list.ToDictionary(e => e, e => false);
and any time you select an item you change the boolean value to true :
dic[selectedObject] = true;
and when you try to select a new item from your dictionary you only have to skip the key/value pairs having a true value.

Is it possible to do start iterating from an element other than the first using foreach?

I'm thinking about implementing IEnumerable for my custom collection (a tree) so I can use foreach to traverse my tree. However as far as I know foreach always starts from the first element of the collection. I would like to choose from which element foreach starts. Is it possible to somehow change the element from which foreach starts?
Yes. Do the following:
Collection<string> myCollection = new Collection<string>;
foreach (string curString in myCollection.Skip(3))
//Dostuff
Skip is an IEnumerable function that skips however many you specify starting at the current index. On the other hand, if you wanted to use only the first three you would use .Take:
foreach (string curString in myCollection.Take(3))
These can even be paired together, so if you only wanted the 4-6 items you could do:
foreach (string curString in myCollection.Skip(3).Take(3))
It's easiest to use the Skip method in LINQ to Objects for this, to skip a given number of elements:
foreach (var value in sequence.Skip(1)) // Skips just one value
{
...
}
Obviously just change 1 for any other value to skip a different number of elements...
Similarly you can use Take to limit the number of elements which are returned.
You can read more about both of these (and the related SkipWhile and TakeWhile methods) in my Edulinq blog series.
You can use Enumerable.Skip to skip some elements, and have it start there.
For example:
foreach(item in theTree.Skip(9)) // Skips the first 9 items
{
// Do something
However, if you're writing a tree, you might want to provide a member on the tree item itself that will return a new IEnumerable<T> which will enumerate from there down. This would, potentially, be more useful in the long run.
If you want to skip reading Rows in a DataGridView, try this
foreach (DataGridViewRow row in dataGridView1.Rows.Cast<DataGridViewRow().Skip(3))
If you want to copy contents of one DataGridView to another skipping rows, try this,
foreach (DataGridViewRow row in dataGridView1.Rows.Cast<DataGridViewRow>().Skip(3))
{
foreach (DataGridViewCell cell in row.Cells)
{
string value = cell.Value.ToString();
dataGridView2.Rows[i].Cells[j].Value = cell.Value.ToString();
j++;
}
i++;
j = 0;
}
this copies the contents from one DataGridView to another skipping 3 rows.
Foreach will iterate over your collection in the way defined by your implementation of IEnumerable. So, although you can skip elements (as suggested above), you're still technically iterating over the elements in the same order.
Not sure what you are trying to achieve, but your class could have multiple IEnumerable properties, each of which enumerates the elements in a specific order.

Categories

Resources