Iterating over a growing dictionary - c#

I am working with C# and I have a dictionary called intervalRecordsPerObject of type Dictionary<string, List<TimeInterval>>. I need to iterate through the dictionary. The problem is: everytime I iterate through the dictionary, more KeyValuePairs may get added to it. As the dictionary grows, I need to keep iterating over the new entries too.
Firstly, I did this: A simple foreach loop that gave me an InvalidOperationException saying
Collection was modified; enumeration operation may not execute.
I know I cannot iterate over the Dictionary this way if it keeps changing as C# converts it with ToList() before foreach loop.
I know I can copy the keys to a temporary array, iterate over the dictionary using simple for loop and Count and whenever a new entry is added to the dictionary, add the corresponding key to the array too. Now, the problem is a simple array cannot grow dynamically and I don't know beforehand what the required size could be.
To move ahead, I thought I'd do this:
List<string> keyList = new List<string>(intervalRecordsPerObject.Count);
intervalRecordsPerObject.Keys.CopyTo(keyList.ToArray(), 0);
I cannot do this either. keyList is currently empty and therefore keyList.toArray() returns an array of length 0 which gives me an ArgumentException saying
Destination array is not long enough to copy all the items in the
collection. Check array index and length.
I am stuck! Any idea what more can I try? Thanks for any help.
Addition 1:
The dictionary stores the time intervals for which a particular object is present. Key is the ID of the object. New entries may get added in every iteration (worst case) or may not get added even once. Whether or not entries are added is decided by a few conditions (whether the object overlaps with some other intervals, etc.). This triggers a change in the ID and the corresponding interval list which is then added as a new entry to the dictionary.

Something like this:
List<string> keys = dict.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{
var key = keys[i];
List<TimeInterval> value;
if (!dict.TryGetValue(key, out value))
{
continue;
}
dict.Add("NewKey", yourValue);
keys.Add("NewKey");
}
The trick here is that you enumerate the List<T> by index! In this way, even if you add new elements, the for (...) will "catch" them.
Other possible solution, by using a temporary Dictionary<,>:
// The main dictionary
var dict = new Dictionary<string, List<TimeInterval>>();
// The temporary dictionary where new keys are added
var next = new Dictionary<string, List<TimeInterval>>();
// current will contain dict or the various instances of next
// (multiple new Dictionary<string, List<TimeInterval>>(); can
// be created)
var current = dict;
while (true)
{
foreach (var kv in current)
{
// if necessary
List<TimeInterval> value = null;
// We add items only to next, that will be processed
// in the next while (true) cycle
next.Add("NewKey", value);
}
if (next.Count == 0)
{
// Nothing was added in this cycle, we have finished
break;
}
foreach (var kv in next)
{
dict.Add(kv.Key, kv.Value);
}
current = next;
next = new Dictionary<string, List<TimeInterval>>();
}

You can access the Keys by positions rather than by content and use a normal For loop (allowing additions/removals without any restriction).
for (int i = 0; i < dict.Keys.Count; i++)
{
string curKey = dict.Keys.ElementAt(i);
TimeInterval curVal = dict.Values.ElementAt(i);
//TimeInterval curVal = dict[curKey];
//Can add or remove entries
}

Related

C# Dictionary - Does adding same set of keys in the same order every time guarantees to be kept in the same order(whatever the order is) every time?

By same order, I don't mean the same order the keys were added!
Say,
1) I've a dictionary d
2) I add 2, 4, 6 as my keys like
d.Add(2, "")
d.Add(4, "")
d.Add(6, "")
3) Now I access it via d.Keys property
Say, it returns keys in order 4, 2, 6
Now by same order I mean, If I keep repeating steps 2 & 3.
Will d.Keys always return the keys in same order 4, 2, 6.
Given the fact that the same set of keys(2,4,6) are added every time and in the same order!
Basically is adding keys in dictionary a deterministic process ?
I've run a few sample random cases, and it appears that Dictionary.Keys property returns the same sequence every time for the same set of keys.
But is it guaranteed ?
Sample code I tried
public static void Main(string[] args)
{
var rand = new Random();
var fixedKeys = Enumerable.Range(1, 100000).Select(x => rand.Next(10000000)).Distinct().ToList();
var dic = new Dictionary<int, string>();
foreach (var item in fixedKeys)
{
dic.Add(item, "");
}
var fixedSequence = dic.Keys;
for (int j = 0; j < 10; j++)
{
dic = new Dictionary<int, string>();
foreach (var item in fixedKeys)
{
dic.Add(item, "");
}
if (!dic.Keys.SequenceEqual(fixedSequence))
{
Console.WriteLine("Order changed");
}
}
}
From the documentation:
Remarks
The order of the keys in the Dictionary<TKey,TValue>.KeyCollection is unspecified, but it is the same order as the associated values in the Dictionary<TKey,TValue>.ValueCollection returned by the Values property.
The order of the keys is not guaranteed to be in any order.
No, it is not guaranteed the keys will be returned in any order.
If you require this property, you may want to look into a SortedDictionary.
It is not guaranteed. The Dictionary is a hashtable and its concept does not enforce the reading to be in the same order as writing.
The order of elements in a dictionary is non-deterministic.
For purposes of enumeration, each item in the dictionary is treated as
a KeyValuePair structure representing a value and its
key. The order in which the items are returned is undefined.
See
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?redirectedfrom=MSDN&view=netframework-4.8
https://stackoverflow.com/a/4007787/194717

Initialize Array of Dictionary Randomly

I am dealing with Array of Dictionaries and this SO Post is really helpful to achieve what I want so far.
But, now I want to initialize Dictionary for an array index based on the output of code.
I have a Dictionary<int,string>, where I am storing a Id as Key. I am having Array of 10 dictionaries as follows:
Dictionary<int, string>[] matrix = new Dictionary<int, string>[10];
So, based on the value of (Id%10), i want to store that record in a respective array. For i.e., if id= 12, I want to store it in matrix[2]. If id = 15, i want to store it in matrix[5].
Now,question is, how to check each time whether a dictionary is initialized for a particular index or not. If yes, than add the record to the dictionary else initialize the instance and then add the record to Dictionary.
Something like following:
if {} // if dict with id%10 is initialized then
{
matrix[id%10].Add();
}
else
{
matrix[id%10] = new Dictionary<int,string>();
matrix[id%10].Add();
}
Edit: I know i can initialize all first using loop, but I want to initialize only when it's necessary.
Dictionary<int, string>[] matrix = new Dictionary<int, string>[10];
int id = 0; // Number here
int index = id % 10;
if (matrix[index] == null)
{
matrix[index] = new Dictionary<int, string>();
}
int key = 0; // key you want to insert
if (matrix[index].ContainsKey(key))
{
// Dictionary already has this key. handle this the way you want
}
else
{
matrix[index].Add(0, ""); // Key and value here
}

C# Auto Update List insted of .clear and refill the list

In my program I populate a list, but the next time I rerun my loop again I .clear the list to repopulate it again because some items in the list might update. How ever, is there somehow I can make it so it just auto updates the items in the list so I can't have to .clear and re add the items?
_players.Clear();
_weapons.Clear();
_entities.Clear();
var localPlayerPtr = Smurf.Memory.Read<IntPtr>(Smurf.ClientBase + Offsets.Misc.LocalPlayer);
LocalPlayer = new LocalPlayer(localPlayerPtr);
LocalPlayerWeapon = LocalPlayer.GetCurrentWeapon(localPlayerPtr);
for (var i = 0; i < _capacity; i++)
{
var entity = new BaseEntity(GetEntityPtr(i));
if (!entity.IsValid)
continue;
if (entity.IsPlayer())
_players.Add(new Player(GetEntityPtr(i)));
else if (entity.IsWeapon())
_weapons.Add(new LocalPlayerWeapon(GetEntityPtr(i)));
else
_entities.Add(new BaseEntity(GetEntityPtr(i)));
}
_lastUpdate = timeStamp;
Total repopulation of a list should be a fast enough way to deal with this. If you really need to go the other way, then your alternative is to when you run your loop:
Check your new element if it exists, and if yes, update it in the list (_players[index] = newPlayer; or something similar). Check by ID, or some other property that identifies your objects.
If it doesn't exist, add it as a new item to the list.
Store your new element in a separate list to keep track. (let's call it changesList)
(optional, if you need to prune) When you've finished going through all your new items, compare changesList with your other lists (you can probably use the Except(IEnumerable<T> list) method, and remove the objects that were not used.

Serially assign values to OrderedDictionary in C#

I have two key-value pairs, and now I want to fill up the larger one with values from the smaller one in a serial manner.
OrderedDictionary pickersPool = new OrderedDictionary(); // Small
OrderedDictionary pickersToTicketMap = new OrderedDictionary(); // Big
pickersPool.Add("emp1", 44);
pickersPool.Add("emp2", 543);
Now I need to update pickersToTicketMap to look like this:
("100", 44);
("109", 543);
("13", 44);
("23", 543);
So basically I need the pickersPool value to cycle through the keys of the pickersToTicketMap dictionary.
I need pickerPool values to keep cycling pickersToTicketMap and updating its value serially.
The pickersToTicketMap orderedlist initially has a value of:
("100", "null");
("109", "null");
("13", "null");
("23", "null");
so I need for the values of PickerPool orderedDictionary to fill up those nulls in a repeated fashion.
It sounds like you should start with a List<string> (or possibly a List<int>, given that they all seem to be integers...) rather than populating your map with empty entries to start with. So something like:
List<string> tickets = new List<string> { "100", "109", "13", "23" };
Then you can populate your pickersToTicketMap as:
var pickers = pickersPool.Values;
var pickerIterator = pickers.GetEnumerator();
foreach (var ticket in tickets)
{
if (!pickerIterator.MoveNext())
{
// Start the next picker...
pickerIterator = pickers.GetEnumerator();
if (!pickerIterator.MoveNext())
{
throw new InvalidOperationException("No pickers available!");
}
}
ticketToPickerMap[ticket] = pickerIterator.Current;
}
Note that I've changed the name from pickersToTicketMap to ticketToPickerMap because that appears to be what you really mean - the key is the ticket, and the value is the picker.
Also note that I'm not disposing of the iterator from pickers. That's generally a bad idea, but in this case I'm assuming that the iterator returned by OrderedDictionary.Values.GetEnumerator() doesn't need disposal.
There may be what you are looking for:
using System.Linq;
...
int i = 0;
// Cast OrderedDictionary to IEnumarable<DictionaryEntry> to be able to use System.Linq
object[] keys = pickersToTicketMap.Cast<DictionaryEntry>().Select(x=>x.Key).ToArray();
IEnumerable<DictionaryEntry> pickersPoolEnumerable = pickersPool.Cast<DictionaryEntry>();
// iterate over all keys (sorted)
foreach (object key in keys)
{
// Set the value of key to element i % pickerPool.Count
// i % pickerPool.Count will return for Count = 2
// 0, 1, 0, 1, 0, ...
pickersToTicketMap[key] = pickersPoolEnumarable
.ElementAt(i % pickersPool.Count).Value;
i++;
}
PS: The ToArray() is required to have a separate copy of the keys, so you don't get a InvalidOperationException due to changing the element you are iterating over.
So you want to update the large dictionary's values with consecutive and repeating values from the possibly smaller one? I have two approaches in mind, one simpler:
You can repeat the smaller collection with Enumerable.Repeat. You have to calculate the count. Then you can use SelectMany to flatten it and ToList to create a collection. Then you can use a for loop to update the larger dictionary with the values in the list via an index:
IEnumerable<int> values = pickersPool.Values.Cast<int>();
if (pickersPool.Count < pickersToTicketMap.Count)
{
// Repeat this collection until it has the same size as the larger collection
values = Enumerable.Repeat( values,
pickersToTicketMap.Count / pickersPool.Count
+ pickersToTicketMap.Count % pickersPool.Count
)
.SelectMany(intColl => intColl);
}
List<int> valueList = values.ToList();
for (int i = 0; i < valueList.Count; i++)
pickersToTicketMap[i] = valueList[i];
I would prefer the above approach, because it's more readable than my second which uses an "infinite" sequence. This is the extension method:
public static IEnumerable<T> RepeatEndless<T>(this IEnumerable<T> sequence)
{
while (true)
foreach (var item in sequence)
yield return item;
}
Now you can use this code to update the larger dictionary's values:
var endlessPickersPool = pickersPool.Cast<DictionaryEntry>().RepeatEndless();
IEnumerator<DictionaryEntry> endlessEnumerator;
IEnumerator<string> ptmKeyEnumerator;
using ((endlessEnumerator = endlessPickersPool.GetEnumerator()) as IDisposable)
using ((ptmKeyEnumerator = pickersToTicketMap.Keys.Cast<string>().ToList().GetEnumerator()) as IDisposable)
{
while (endlessEnumerator.MoveNext() && ptmKeyEnumerator.MoveNext())
{
DictionaryEntry pickersPoolItem = (DictionaryEntry)endlessEnumerator.Current;
pickersToTicketMap[ptmKeyEnumerator.Current] = pickersPoolItem.Value;
}
}
Note that it's important that I use largerDict.Keys.Cast<string>().ToList(), because I can't use the original Keys collection. You get an exception if you change it during enumeration.
Thanks to #jon skeet, although he modified my objects too much while trying to provide a hack for this.
After looking at your solution, I implemented the following, which works well for all my objects.
var pickerIterator = pickerPool.GetEnumerator();
foreach (DictionaryEntry ticket in tickets)
{
if (!pickerIterator.MoveNext())
{
// Start the next picker...
pickerIterator = pickerPool.GetEnumerator();
if (!pickerIterator.MoveNext())
{
throw new InvalidOperationException("No pickers available!");
}
}
ticketToPickerMap[ticket.Key] = pickerIterator.Value.ToString();
}

Reassigning dictionary elements through loop

I have a Dictionary. I've used a loop to add elements in the dictionary and changed them over time through code. How can I revert the elements back to their initial value?
My code:
//for adding to the dictionary
Dictionary<string, string> D = new Dictionary<string, string>();
for(i=0; i<DataSet1.Tables[0].Rows.Count; i++)
{
row = DataSet1.Tables[0].Rows[i];
id[i]=row["UserID"].ToString();
D.Add(id[i], "absent");
}
//for reassigning back to absent
for(int i =0; i<D.Count; i++) D[i.ToString()]="absent";
My reassigning loop instead of reassign seems to just add new elements. Initialy counted about 1000 and after the reassign loop it's about 3000.. P.S. Using c#
Please help
You can enumerate keys of your dictionary and set entries values:
foreach(string key in D.Keys.ToArray())
D[key] = "absent";
Also creation of dictionary can be simplified to (with help of Linq to DataSet):
var D = DataSet1.Tables[0].AsEnumerable()
.ToDictionary(r => r.Field<string>("UserID"),
r => "absent");
Give this a try:
foreach(var key in d.Keys.ToArray())
{
d[key] = "absent";
}
The .ToArray() is needed because the foreach loop will throw an error before the second iteration if you try to iterate on d.Keys directly.

Categories