Why doesn't the code below clear all array list data?
Console.WriteLine("Before cleaning:" + Convert.ToString(ID.Count));
//ID.Count = 20
for (int i = 0; i < ID.Count; i++)
{
ID.RemoveAt(i);
}
Console.WriteLine("After cleaning:" + Convert.ToString(ID.Count));
//ID.Count = 10
Why is 10 printed to the screen?
Maybe there is another special function, which deletes everything?
You're only actually calling RemoveAt 10 times. When i reaches 10, ID.Count will be 10 as well. You could fix this by doing:
int count = ID.Count;
for (int i = 0; i < originalCount; i++)
{
ID.RemoveAt(0);
}
This is an O(n2) operation though, as removing an entry from the start of the list involves copying everything else.
More efficiently (O(n)):
int count = ID.Count;
for (int i = 0; i < originalCount; i++)
{
ID.RemoveAt(ID.Count - 1);
}
or equivalent but simpler:
while (ID.Count > 0)
{
ID.RemoveAt(ID.Count - 1);
}
But using ID.Clear() is probably more efficient than all of these, even though it is also O(n).
`Array.Clear()`
removes all items in the array.
`Array.RemoveAt(i)`
removes the element of ith index in the array.
ArrayList.Clear Method
Removes all elements from the ArrayList.
for more detail : http://msdn.microsoft.com/en-us/library/system.collections.arraylist.clear.aspx
After removing 10 items, ID.Count() == 10 and i == 10 so the loop stops.
Use ID.Clear() to remove all items in the array list.
Use the clear() Method
or
change ID.RemoveAt(i); to ID.RemoveAt(0);
Whenever an element is removed from the collection, its index also changes. Hence when you say ID.RemoveAt(0); the element at index 1 now will be moved to index 0. So again you've to remove the same element (like dequeuing). until you reach the last element. However if you want to remove all the elements at once you can better use the Clear() method.
Your code does:
ID.RemoveAt(0);
...
ID.RemoveAt(9);
ID.RemoveAt(10); \\ at this point you have already removed 10
\\ items so there is nothing left on 10- 19, but you are left with
\\ the 'first' 10 elements
...
ID.RemoveAt(19);
Generally speaking your method removes every second element from the list..
Use ArrayList.Clear instead as other have mentioned.
Related
Hi I have a problem with a for loop.
It looks like this
for (int i = 0; i < ObjectManager.Instance.Objects.Count; i++)
{
if (ObjectManager.Instance.Objects[i] is Asteroid)
{
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
}
}
But the count gets shorter while I remove objects, which causes the loop to end prematurely. Is there a way to do this without a bunch of extra loops.
Why don't you loop backward?
// Just change the order from Count - 1 down to 0
for (int i = ObjectManager.Instance.Objects.Count - 1; i >= 0; --i)
{
if (ObjectManager.Instance.Objects[i] is Asteroid)
{
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
}
}
In case you have to loop forward (e.g. if Instances should be deleted in the order they are created because they are depend on each other) you can modify for loop in this way:
for (int i = 0; i < ObjectManager.Instance.Objects.Count;) // <- No increment here
if (ObjectManager.Instance.Objects[i] is Asteroid)
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
else
i += 1; // <- Increment should be here!
Yet another possibility is Linq:
ObjectManager.Instance.Objects.RemoveAll(item => item is Asteroid);
Three options:
If ObjectManager.Instance.Objects is a List<T>, use List<T>.RemoveAll with a predicate, making your code much simpler:
// This replaces your whole loop...
ObjectManager.Instance.Objects.RemoveAll(x => x is Asteroid);
Count from the end of the collection rather than from the start, so that you don't need to adjust the index afterwards:
for (int i = ObjectManager.Instance.Objects.Count - 1; i >= 0; i--)
Just decrement i after calling Remove, so that you'll look at the right index on the next iteration.
Note that in the second and third options your code will be a lot simpler to read if you extract the expression ObjectManager.Instance.Objects into a local variable before you use it 4 times. Also consider using RemoveAt(i) rather than Remove(instances[i]), assuming RemoveAt is available for the type you're using.
My current code:
Remove()
{
for (int i = 0; i < ConGridView.RowCount; i++)
{
if (ConGridView.Rows[i].Cells[0].Value.ToString() == Address)
{
ConGridView.Rows.RemoveAt(i);
break;
}
}
}
So what I am trying to call the remove function every time a client disconnect. the function will remove the connection address from the datagridview. It works well when clients are disconnection one by one. However, if 100 connections gets dropped and it tries to remove 100 connections in less than a second, than it errors out saying "Row Index provided is out of range". How should I check for that ?
So far I've tried:
Try, catch.
if (ConGridView.Rows[i] != null), if (i < ConGridView.RowCount)
None of it seem to work so far. I've also got results using (i < ConGridView.RowCount) where i is 26 while RowCount is 24, but the remove at function still activates..
Any idea on this ?
You can't do this. Your code loops through all the rows in ConGridView, but it deletes them as you do. Therefore, at some point you will try to access an item you have deleted, which will cause the error you described.
Probably the best approach it to iterate through the rows in reverse order. This way, deleting a row at the end won't affect when you access rows at the start.
The problem is you initialise your for loop with the current count of rows and then start removing those same rows from the datagridview. At some point your for loop will try to remove a row at an index that is greater than the number of rows left.
Try this instead:
for (int i = ConGridView.RowCount - 1; i >= 0; i--)
{
if (ConGridView.Rows[i].Cells[0].Value.ToString() == Address)
{
ConGridView.Rows.RemoveAt(i);
break;
}
}
why dont you get the total count to a separate variable and then iterate
Remove()
{
int totalConnections = ConGridView.RowCount;
for (int i = 0; i < totalConnections ; i++)
{
if (ConGridView.Rows[i].Cells[0].Value.ToString() == Address)
{
ConGridView.Rows.RemoveAt(i);
break;
}
}
}
This issue is becuase you are modifying the collection your are iterating over. It will be better if you use a temporary array and two loops to remove your entries.
Remove()
// You can use an array/list or whatever you want below.
Collection<DataGridViewRow> rowsToDelete = new Collection<DataGridViewRow>();
for (int i = 0; i < ConGridView.RowCount; i++)
{
if (ConGridView.Rows[i].Cells[0].Value.ToString() == Address)
{
rowsToDelete.Add(ConGridView.Rows[i]);
break;
}
}
// now remove the marked entries.
foreach(DataGridViewRow deletedRow in rowsToDelete)
{
ConGridView.Rows.Remove(deletedRow);
}
When you remove an item from an array, it is reconstructed; shifting the remaining elements up by one to remove the gap of the index you have removed.
1. guybrush threepwood
2. murray
3. elaine
4. Jimmy Gibbs Jr.
If you remove 2. item in here; it becomes this:
1. guybrush threepwood
2. elaine
3. Jimmy Gibbs Jr.
When you are iterating, imagine:
for (int i = 0; i < myArray.Count; i++)
{
if (i == 2) myArray.RemoveAt(i);
}
While running this, when i = 3, the element at 3 has changed, you expect it to be 'elaine' but it is 'Jimmy Gibbs Jr.'. One way to fix this is decrease i by one if we delete it, making sure that i refers to correct value.
for (int i = 0; i < myArray.Count; i++)
{
if (i == 2)
{
myArray.RemoveAt(i);
i--;
}
}
I would go for LINQ in this case, though, everything is easier with that.
myArray.RemoveAll(x => x == "murray");
I've tried all the suggestions posted by everyone here, however, the error was still there.
I've solved the problem using a different way... I've switched to TreeNodeView since that's what I was going to use ultimately. Now I can remove as many connection as i want with:
For each(TreeNode TN in ConTreeView)
{
ConTreeView.Nodes.Remove(TN);
}
I'm trying to see if when a user enters some text it searches the array for any matches, and whatever doesn't match gets removed from the array;
string search = textBox1.Text;
for (int i = 0; i < staffUsers.Count; i++)
{
if (!(staffUsers[i].staff_name.Contains(search)))
{
staffUsers.Remove(staffUsers[i]);
}
}
I have some rubbish names in my array 'Rob Dob','Joe Bloggs', 'h h', 'ghg hgh', and if the search variable ended up being 'R', Joe Bloggs would get removed but 'h h' and 'ghg hgh' stay there, but there is no R involved there at all? any reason why>?!
You have to iterate backwards in order to remove from an array. Every time you remove an item, your array gets smaller. By going backwards, that fact does not matter.
string search = textBox1.Text;
for (int i = staffUsers.Count - 1; i >= 0 ; i--)
{
if (!(staffUsers[i].staff_name.Contains(search)))
{
staffUsers.Remove(staffUsers[i]);
}
}
The problem with your code is that you are removing items as you iterate over it. You remove an item, but keep iterating, even though the size of the array changes when you remove an item.
You need to reset your i value after you remove something. Alternatively, you need to use a built in to do the heavy lifting:
staffUsers.RemoveAll(i => !(i.staff_name.Contains(search)));
Uses a tiny LINQ expression to do the work. Remove all items where that predicate matches. i represents an item to apply the expression to. If that expression evaluates to true, away it goes.
Long story short, whenever you remove an item at index [i], you skip the item at index [i+1]. For example, if your array looks like:
{'Joe Bloggs', 'Rob Dobb', 'h h', 'gafddf'}; i=0
remove Joe Bloggs, which is at position 0.
{Rob Dobb', 'h h', 'gafddf'}; i=1
remove 'h h', which is at position 1
{Rob Dobb', 'gafddf'}; i=2
i is not less than yourArray.Count, so the loop stops. There is no position 2.
The quickest fix is to add i-- if you remove something from index [i]. In your case,
staffUsers.Remove(staffUsers[i]);
i--;
Hope this helps!
In each iteration of the loop, either remove an item or increment the counter, not both operations. Otherwise, you'll skip the next array element whenever you remove an array element:
string search = textBox1.Text;
for (int i = 0; i < staffUsers.Count;)
{
if (!(staffUsers[i].staff_name.Contains(search)))
{
staffUsers.Remove(staffUsers[i]);
}
else
{
i++;
}
}
The simplest way to solve the above problem is using LINQ.
Following code disentangle above problem.
string search = textBox1.Text;
staffUsers= (from user in staffUsers
where !user.Contains(search)
select user).ToArray<string>();
Note: I assumed staffUsers is array of string.
string search = textBox1.Text;
for (int i = 0; i < staffUsers.Count; i++)
{
if (!(staffUsers[i].staff_name.Contains(search)))
{
staffUsers.Remove(staffUsers[i]);
// reset the index one stepback
i--;
}
}
I've read a couple of articles stating that List.RemoveAt() is in O(n) time.
If I do something like:
var myList = new List<int>();
/* Add many ints to the list here. */
// Remove item at end of list:
myList.RemoveAt(myList.Count - 1); // Does this line run in O(n) time?
Removing from the end of the list should be O(1), as it just needs to decrement the list count.
Do I need to write my own class to have this behavior, or does removing the item at the end of a C# list already perform in O(1) time?
In general List<T>::RemoveAt is O(N) because of the need to shift elements after the index up a slot in the array. But for the specific case of removing from the end of the list no shifting is needed and it is consequently O(1)
Removing last item will actually be O(1) operation since only in this case List doesn't shift next items in array. Here is a code from Reflector:
this._size--;
if (index < this._size) // this statement is false if index equals last index in List
{
Array.Copy(this._items, index + 1, this._items, index, this._size - index);
}
this._items[this._size] = default(T);
This should give you an idea
public void RemoveAt(int index) {
if ((uint)index >= (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
_size--;
if (index < _size) {
Array.Copy(_items, index + 1, _items, index, _size - index);
}
_items[_size] = default(T);
_version++;
}
When speaking asymptotically, O(N) is the worst case time complexity of the method itself, where N is the count. It cannot perform worse than that.
Practically, it would be in the order of O(N-I) (ignoring constant time overhead), where I is the index. This is deducible since all the items beyond the given index I needs to be shifted to position preceding them respectively in a List.
To see this intuitively, if N is 100 and index is 99 (last element), then there are no elements that need to be 'shifted' just the last element is deleted (or simply the count is decreased without changing the size of data structure).
Similarly, when N is 100, and index is 0 (first element), 99 shifts have to be made.
Run the following code and see for yourself:
int size = 1000000;
var list1 = new List<int>();
var list2 = new List<int>();
for (int i = 0; i < size; i++)
{
list1.Add(i);
list2.Add(i);
}
var sw = Stopwatch.StartNew();
for (int i = 0; i < size; i++)
{
list1.RemoveAt(size-1);
list1.Add(0);
}
sw.Stop();
Console.WriteLine("Time elapsed: {0}", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < size; i++)
{
list2.RemoveAt(0);
list2.Add(0);
}
sw.Stop();
Console.WriteLine("Time elapsed: {0}", sw.ElapsedMilliseconds);
It seems to me that if this was actually relevant to your application, you could have measured it in less time than it took to ask the question. And now you have at least two contradictory answers, so you'll have to test it anyway.
The point I'm trying to make is that unless the MSDN docs say that removeAt is O(1) for items at the end of the list, you couldn't really count on it working that way, and it might change in any given .NET update. For that matter, the behavior could be different for different types, for all you know.
If List is the "natural" data structure to use, then use it. If removing items from the List ends up being a hot spot n your profiling, then maybe it's time to implement your own class.
I have a list of items to remove from an ordered collection in C#.
what's the best way in going about this?
If I remove an item in the middle, the index changes but what If I want to remove multiple items?
To avoid index changes, start at the end and go backwards to index 0.
Something along these lines:
for(int i = myList.Count - 1; i >= 0; i++)
{
if(NeedToDelete(myList[i]))
{
myList.RemoveAt(i);
}
}
What is the type of the collection? If it inherits from ICollection, you can just run a loop over the list of items to remove, then call the .Remove() method on the collection.
For Example:
object[] itemsToDelete = GetObjectsToDeleteFromSomewhere();
ICollection<object> orderedCollection = GetCollectionFromSomewhere();
foreach (object item in itemsToDelete)
{
orderedCollection.Remove(item);
}
If the collection is a List<T> you can also use the RemoveAll method:
list.RemoveAll(x => otherlist.Contains(x));
Assuming that the list of items to delete is relatively short, you can first sort the target list. Than traverse the source list and keep an index in the target list which corresponds to the item which you deleted.
Supposed that the source list is haystack and list of items to delete is needle:
needle.Sort(); // not needed if it's known that `needle` is sorted
// haystack is known to be sorted
haystackIdx = 0;
needleIdx = 0;
while (needleIdx < needle.Count && haystackIdx < haystack.Count)
{
if (haystack[haystackIdx] < needle[needleIdx])
haystackIdx++;
else if (haystack[haystackIdx] > needle[needleIdx])
needleIdx++;
else
haystack.RemoveAt(haystackIdx);
}
This way you have only 1 traversal of both haystack and needle, plus the time of sorting the needle, provided the deletion is O(1) (which is often the case for linked lists and the collections like that). If the collection is a List<...>, deletion will need O(collection size) because of data shifts, so you'd better start from the end of both collections and move to the beginning:
needle.Sort(); // not needed if it's known that `needle` is sorted
// haystack is known to be sorted
haystackIdx = haystack.Count - 1;
needleIdx = needle.Count - 1;
while (needleIdx >= 0 && haystackIdx >= 0)
{
if (haystack[haystackIdx] > needle[needleIdx])
haystackIdx--;
else if (haystack[haystackIdx] < needle[needleIdx])
needleIdx--;
else
haystack.RemoveAt(haystackIdx--);
}