Initialize Array of Dictionary Randomly - c#

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
}

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

Is there a way to give auto generated value(incremental integer) to a value in Dictionary key value pair In C#

Created a dictionary with key as string and value as integer.
My code is as follows. Let's consider s is a string in foreach(string s in wordarray)
Dictionary<string,int> wordmap = new Dictionary<string,int>();
wordmap.Add(s, GetValueOrDefault(s, 0) + 1);
Please provide solution to add auto incremented values for the dictionary
Firstly you cant use GetValueOrDefault(s,0) that is mistake.Because this function accept just one parameter
You dont need to use it when you add value to dictionary.If you want to add value to dictionary you can create a variable such as default 0 and you increment and add to dictionary below;
Dictionary<string, int> wordmap = new Dictionary<string, int>();
int value = 0;
foreach (string s in wordarray)
{
wordmap.Add(s, value);
value += 1;
}

Array of dictionaries with Unity and Firebase database - Get values from key

First of all - I am pretty new to Unity.
I am trying to retrieve some data from my firebase database, store the data in an array/list of dictionaries, and then use the array/list to show the data from my server.
So... My way of trying to do:
1: Create array of dictionaries to hold my data:
[System.Serializable]
public class Global
{
public static Dictionary<string, object>[] offers;
}
2: Handle the data from the database, and store it in the array:
void Handle_ChildAdded(object sender, ChildChangedEventArgs e)
{
if (e.DatabaseError != null)
{
Debug.LogError(e.DatabaseError.Message);
return;
}
// Do something with the data in args.Snapshot
if (e.Snapshot.Value != null)
{
var dict = e.Snapshot.Value as Dictionary<string, object>;
if (dict != null)
{
Debug.Log(dict);
Global.offers = new Dictionary<string, object>[Global.storesCount+1];
Global.offers[Global.storesCount] = dict;
Global.storesCount++;
hasHaded = true;
}
}
}
So now I have alle the snapshots from my database in my Global.offers array. The struckture of the snapshot i should get look something likes this:
Time to show the data from my array
Everything works fine until this point - Because now I need to show the data I just stored in my Global.offers array.
I try to do that with a loop. I loop though the array and search for the keys from my database and Instantiate the data inside a prefab of a gameobject like this:
for (int i = 0; i < Global.storesCount; i++)
{
Transform scrollViewObj = Instantiate(prefab, new Vector3(0, (downSize * i) - firstY, 0), Quaternion.identity);
scrollViewObj.transform.SetParent(scrollContent.transform, false);
scrollViewObj.transform.Find("Overskift").gameObject.GetComponent<Text>().text = Global.offers[i]["Store"] as string;
scrollViewObj.transform.Find("Text (1)").gameObject.GetComponent<Text>().text = Global.offers[i]["Headline"] as string;
scrollViewObj.transform.Find("Text (2)").gameObject.GetComponent<Text>().text = "Din pris: " + Global.offers[i]["Price"] as string + " kr.";
scrollViewObj.transform.Find("Text (3)").gameObject.GetComponent<Text>().text = "Spar: " + Global.offers[i]["AndresPris"] as string + " kr.";
}
This is where I get the trouble. For some reason Global.offers[i]["Store"] as string == null, witch of course means that I can't instantiate the object. I get this error:
NullReferenceException: Object reference not set to an instance of an object
LoadOffers.Start () (at Assets/Scripts/LoadOffers.cs:36)
It is so weird, because when I try to debug I get some conflicting results:
The length of the array is 19. So it is not empty.
When I try to Debug.Log the array out I get:
System.Collections.Generic.Dictionary`2[System.String,System.Object][]
But when I search for values with keys like:
Debug.Log(Global.offers[0]["Store"]);
All I get is null. Am I searching wrong after the values? Or can anyone else see what I am doing wrong?
The Problem
Your main problem is that Global.offers
is an array and therefore has a fixed length and can not be enlarged dynamically that simple (you would have to copy the array everytime!)
You kind of tried to solve this in Handle_ChildAdded but in the line
Global.offers = new Dictionary<string, object>[Global.storesCount+1];
what you actually do is creating a new empty(!) array of dictionaries with length Global.storeCount+1. You don't copy the current values to a new array.
so after all executions what you get is an array with only null. Only the last item of the array will have the content coming from
Global.offers[Global.storeCount] = dict;
so only the last value will actually be set. It looks somewhat like
{null, null, null, null, ..., Dictionary<string, object> }
that's why the length is a correct value. Your exception is actually already thrown the moment you try to access
Global.offers[0]
which is null.
Solution
I would recommend not to use an array at all but instead a List which in contrary to the array can grow dynamically
[System.Serializable]
public class Global
{
public static List<Dictionary<string, object>> offers = new List<Dictionary<string, object>>();
}
and than before starting to fill the list by adding all dictionaries you should actually somwhere call
Global.offers.Clear();
to reset the list otherwise it keeps growing bigger and bigger with every loading of the database. Also if you are absolutely sure you call this only once you might not need this I would always recommend to do it in order to have a clean, in general working solution.
And later add your elements in
void Handle_ChildAdded(object sender, ChildChangedEventArgs e)
{
if (e.DatabaseError != null)
{
Debug.LogError(e.DatabaseError.Message);
return;
}
// Do something with the data in args.Snapshot
if (e.Snapshot.Value != null)
{
var dict = e.Snapshot.Value as Dictionary<string, object>;
if (dict != null)
{
Debug.Log(dict);
Global.offers.Add(dict);
hasHaded = true;
}
}
}
Later you do access elements in a List the same way as in an array
Debug.Log(Global.offers[0]["Store"]);
Note that you also don't need the variable storeCount neither in your solution - you could have simply used
Global.offers.Length
nor in this new solution - you can simply use
Global.offers.Count

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();
}

Iterating over a growing dictionary

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
}

Categories