What element does ConcurrentDictionary.ElementAt return - c#

In my code I got an ConcurrentDictionary now I want to iterate over each element in the Dictionary, but if a condition is true I want to remove an element from this Dictionary so I can't use a foreach loop. Also it might happen that the Dictionary will get a new element while in the loop or get one removed from a different thread. After some research I ended up using ElementAt.
Now my question is if the ConcurrentDictionary will release the indexes again like a List does. So that the first element will always have the index 0.
This is what my code looks like, CommandHandler.Timeouter is from ConcurrentDictionary:
int current = 0;
while (CommandHandler.Timeouter.Count() > current)
{
var info = CommandHandler.Timeouter.ElementAt(current);
var timeoutcooldown = info.Value.LastCommandTime.AddMinutes(1);
if (timeoutcooldown < DateTime.UtcNow)
{
CommandHandler.Timeouter.TryRemove(info.Key, out _);
}
else current++;
}

ElementAt just treats the dictionary as an IEnumerable<KeyValuePair<TKey, TValue>>. Dictionaries are not ordered. The index is therefore meaningless. Think of the elements coming back in random order each time. Also, ElementAt has no way to make this thread safe.
It seems you want to implement cache expiration. Consider just using lock to access a normal dictionary. If there is not much contention this will be the simplest solution and very fast.
An alternative code pattern to this loop would be this:
var itemsToExpire = myDict.Where(/* compute expiration */).ToList();
foreach (var item in itemsToExpire)
myDict.Remove(item);
No need for any complicated looping.

Related

C# best approach to check a value is exist in a List Several times

I have list of integers and I want to know a value exist in my list Several times.
What is the best approach to do this?
catching data using LookUp or dictionary or HashMap or ...?
Example:
List<int> samples = {5,4,6,2,1}
// if(2 exist in samples) do something ...
// if(3 exist in samples) do something ...
// if(5 exist in samples) do something ...
// if(8 exist in samples) do something ...
// if(13 exist in samples) do something ...
// if ....
You can store them in HashSet and check whether value exists with O(1):
var unique = new HashSet<int>(){ 5,4,6,2,1};
var hasValue = unique.Contains(1);
and then just check:
if (unique.Contains(2))
// do something ...
In addition, HashSet<T> prevents storing duplicates, so it is extremely fast.
UPDATE:
List<T> will search with O(N). Why? Because Big O Notation should consider the worst case of time complexity. Let's imagine we have the following list:
var numbers = new List<int> { 5, 4, 6, 2, 1 };
and we want to find number 1. So Contains() method of List<T> has to iterate the whole array until it finds number 1. So we have O(N).
LinkedList<T> will search with O(N). Why? The reason is the same like in List<T>. However, LinkedList<T> does not have an array under the hood, it has a class which has a pointer to next element and next element has pointer to the next element and so on. We have to iterate all elements to find an item.
HashSet<T> will search with O(1). Why? The reason is HashSet<T> under the hood will not iterate through array. It will run internal method InternalGetHashCode which returns position of number in array. You can see the source code here.
In addition, there is a very nice answer about How can hashset.contains be O(1) with this implementation?
It depends on what you mean by best. If you are using a list, you could use the Exists() method.
if(samples.Exists(value => value == valueToCheck)){
//Do work
}

For Each - Inverted Order

I have a List<Object>. I want to iterate over this list, but I want the order to be inverted, so when I use the object, it will start from the last to the first. How can I do it, minimal code?
Thanks.
P.S. I'm using C#, WinForms.
Use the extension method Enumerable<T>.Reverse. This will iterate through the list in a reverse order and leave the original list intact.
foreach(var item in list.AsEnumerable().Reverse())
{
}
Reverse , however, traverses the list and caches your items in a reverse order when iteration starts. In 90% of the cases this is fine, because it's still a O(n) operation, but if you want to avoid this cache just use a plain old for
for(int i = list.Count - 1; i >= 0; i--) { }

Going back one iteration in foreach loop in C#

I have a foreach loop that iterates different levels of a treeView in C# that its simplified version looks like this:
foreach (TreeNode childNode in currentNode.Nodes)
{
if (!someCondition)
{
currentNode.Remove();
}
}
but the problem is that when a node (for example from a list of node1, node2, node3 and node4) is removed the list becomes shorter and the foreach loop skips one iteration (for example, say if node2 was to be removed the list becomes node1, node3 and node4 and the next node that the foreach loop considers will be node4 instead of node3). This is because the framework is storing these nodes in an array/list so I would like to know if it is possible to make a foreach loop go back one iteration when I want to remove a node from the tree.
I'm pretty new in .NET framework so your help is really appreciated.
The desired result can perhaps be achieved using Linq by setting
currentNode.Nodes = currentNode.Nodes.Where( n => SomeCondition( n ) ).ToList();
or something similar, so no explicit iteration is necessary. A less elegant solution is using an explicit for-loop running backwards, so that the loop index cannot become invalid. However I would consider this bad practice when a more structural approach is available.
You can use for loop here:
// Pay attention to reversed order:
// each currentNode.Remove() changes currentNode.Nodes.Count
for (int i = currentNode.Nodes.Count - 1; i >= 0; --i) {
TreeNode childNode = currentNode.Nodes[i];
if (!someCondition) {
currentNode.Remove();
}
}
No this is not possible because the iterations of a foreach loop aren't "indexed" in a strict sense.
A for loop is, however, indexed because you provide it with a counting mechanism yourself. There you can change your counter.
Usually it's not a great idea to modify the collection that you are iterating through within a foreach. You should consider using a for loop instead and manually keep track of the current index.

how properly remove item from list [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
Exception during iteration on collection and remove items from that collection
How to remove elements from a generic list while iterating around it?
Better way to remove matched items from a list
// tmpClientList is List<Client> type
if (txtboxClientName.Text != "")
foreach (Client cli in tmpClientList)
if (cli.Name != txtboxClientName.Text)
tmpClientList.Remove(cli);
Error: "Collection was modified; enumeration operation may not execute."
How can i remove items from the list, in some simple way, without saving indexes of these items in another list or array, and removing them in another place in the code. Tried also RemoveAt(index) but it's exactly the same situation, modifying when loop runs.
Move backwards through the list.. that way removing an item does not affect the next item.
for(var i=tmpClientList.Count-1;i>=0;i--)
{
if (tmpClientList[i].Name != txtboxClientName.Text)
tmpClientList.RemoveAt(i);
}
On a List<T>, there is a RemoveAll method that takes a delegate to indicate whether to remove the item. You can use it like this:
tmpCLientList.RemoveAll(cli => cli.Name != txtboxClientName.Text);
Either use a for/while loop, or tmpClientList.RemoveAll(a => a.Name == txtboxClientName.Text). As you didn't specify which c# version you are using, ymmw.
Don't use foreach. Use for and descend the list (i.e. start from the end), using RemoveAt.
So,
// tmpClientList is List<Client> type
if (txtboxClientName.Text != "")
foreach (int pos = tmpClientList.Length - 1; pos >= 0; pos--)
{
Client cli = tmpClientList[pos];
if (cli.Name != txtboxClientName.Text)
tmpClientList.RemoveAt(pos);
}
The problem is that you are trying the modify the list in a foreach iteration. Replace that with a for and you should be ok.
Also, since you seem to be using user input for the name, consider cleaning up the input a bit, at least with a Trim() to remove extra white spaces. If you don't, 'John ' and 'John' will be two different things.
Same for the initial != "" check.
You can create another list with the items you want to delete and iterate the new list to remove items from your "txtboxClientName" list.
Actually, foreach uses Enumerators to iterate through given Item-Collections. Going further the System.Collections.Generic.List<T> implements the IEnumarable-Interface to provide a Class, that knows how to iterate through the items of the list, i.e. the Enumerator. Now if you iterate through that list by using foreach the Enumerator keeps track of the current position, how to reach the next position and some other stuff. The internal logic could be something like storing the number of items in a variable n and then access all objects from 0 to n-1. As you may notice if any object is removed between the iteration steps we shall end in a NullReferenceException when the Enumerator tries to deliver the last object of the list. So to prevent any iteration failures, the list itself is not allowed to be modified during Enumeration.
Hope I was able to state that out at least a little bit comprehensively. :-)

Random Access by Position of Key/Value Pairs in .NET (C#)

I am currently developing a program that uses C#'s Dictionary container (specifically, SortedDictionary). This container works very well for my purposes except for one specific case because I want random access. Specifically, I am generating a random position using a pseudorandom number generator and I need to be able to access that value in the SortedDictionary. At the point that this happens, I do not have a key value.
I could potentially switch to a List which would solve this problem, but would create problems in the rest of the algorithm where SortedDictionary works quite well. Any suggestions/solutions would be much appreciated.
I am currently developing Visual Studio 2005.
Thank you.
You can use a SortedList and it has a Values collection which you may access through an integer index.
public TValue GetRandomElement<TKey, TValue>(SortedDictionary<TKey, TValue> dict)
{
Random randGen = new Random();
int randIndex = randGen.Next(dict.Values.Count);
int i = 0;
foreach (TValue value in dict.Values)
{
if (i++ == randIndex)
return value;
}
// this shouldn't happen unless I have a bug above or you are accessing the dictionary from multiple threads
return default(TValue);
}
Blindly enumerating the ValueCollection is not the most efficient thing in the world. But it gets the job done. If this is a frequent operation in your scenario, you should consider a hybrid data structure that has the performance characteristics needed for both dictionary lookup and random access.
Linq could do this for you:
int n = GetRandomIndex();
object item = dictionary.ElementAt(n).Value;
You don't provide enough information to come up with a solution. How many elements, how often are you going to do this, do you have memory/speed constraints? BTree, SortedList, inserting special nodes in the SortedDictionary could all be useful
Will pulling a random key work?
var randValue = myDictionary.Values.ToList()[myRandomInt];
Edit:
Seems the keys collection and values collection are both IEnumerables so you can't use [] operators. This is the best it gets it seems.
Edit:
Without Linq... Perhaps expensive, but you could copyto array and then pull a value at an index
System.Collections.Generic.KeyValuePair<string, int>[] dictCopy = new System.Collections.Generic.KeyValuePair<string, int>[myDictionary.Count];
myDictionary.CopyTo(dictCopy, 0);
var randValue = dictCopy[myRandomInt].Value;

Categories