I'd like to create a class which contains a generic dictionary which value is an generic dictionary as below:
class DList<T>
{
public Dictionary<string, Dictionary<K, T>> Dic;
public Init<K,T>()
{
Dic = new Dictionary<string, Dictionary<K, T>>();
Dic.Add("Name", new Dictionary<string, T>());
Dic.Add("Id", new Dictionary<int, T>());
}
}
How can I implement this? Thanks a lot
dynamic works. What I need is almost like a multi-keys Dictionary.
public class DList<T> : IEnumerable
{
private List<T> list;
private Dictionary<string, dynamic> dic;
public T this[int i] => list[i];
public dynamic this[string j] => dic[j];
public int Count => list.Count;
public DList(params string[] properties)
{
this.list = new List<T>();
this.dic = new Dictionary<string, dynamic>();
foreach (var item in properties)
{
this.dic.Add(item, new Dictionary<dynamic, T>());
}
}
public void Add(T t)
{
list.Add(t);
foreach (var item in dic)
{
dynamic d = t.GetType()
.GetField(item.Key)
.GetValue(t);
if (item.Value.ContainsKey(d))
continue;
item.Value.Add(d, t);
}
}
public T Get(string key, dynamic s)
{
return dic[key][s];
}
public T Get(int index)
{
return list[index];
}
public void Clear()
{
this.list.Clear();
foreach(var item in dic)
{
item.Value.Clear();
}
}
public void Sort(Comparison<T> Compare)
{
list.Sort(Compare);
}
public IEnumerator GetEnumerator()
{
return list.GetEnumerator();
}
}
class DList<K, T>
{
public Dictionary<string, Dictionary<K, T>> Dic;
public DList()
{
Dic = new Dictionary<string, Dictionary<K, T>>();
Dic.Add("Name", new Dictionary<K, T>());
Dic.Add("Id", new Dictionary<K, T>());
}
}
class DList2<K, T>
{
public Dictionary<string, Dictionary<object, T>> Dic;
public DList2()
{
Dic = new Dictionary<string, Dictionary<object, T>>();
Dic.Add("Name", new Dictionary<object, T>());
Dic.Add("Id", new Dictionary<object, T>());
}
}
If you know K upfront, use the DList approach. If you want to use string, int etc for K, use the approach in DList2. The inner dictionary will work just fine with keys of type int, string, etc, even if its key is declared as object.
Related
I have a config with thousands of EnumValues and I parse them in runtime
void DoSmth()
{
foreach (var line in configLines)
{
var myEnumValue = (MyEnum) Enum.Parse(line);
...
}
}
and I can improve perfomance by creating a map between a string from the config and an actual enumValue
Dictionary<string, MyEnum> dict = new();
void DoSmth()
{
foreach (var line in configLines)
{
if (!dict.ContainsKey(line)
dict.Add(line, (MyEnum) Enum.Parse(typeof(MyEnum), line));
var myEnumValue = dict[line];
...
}
}
Q:
Is there any way (maybe using some co/contravariance magic) to create a generic function so it could create such dictionaries dynamically to avoid writing the same caching code over and over again?
e.g.
void DoSmth()
{
foreach (var line in configLines)
{
var myEnumValue = MyExtensions.Parse<MyEnum>(line);
...
}
}
class MyExtensions
{
Dictionary<Type, Dictionary<string, EnumValue> _cachedEnumValues; // < EnumValue type not exists, so how to?
public T Parse<T>(string s) where T : Enum
{
if (!_cachedEnumValues.ContansKey(typeof(T))
_cachedEnumValues.Add(typeof(T), new Dictionary<string, T>();
if (!_cachedEnumValues[typeof(T)].ContansKey(s))
_cachedEnumValues[typeof(T)].Add(s, (MyEnum) Enum.Parse(typeof(MyEnum), s);
return _cachedEnumValues[typeof(T)][s];
}
}
Sure:
public sealed class EnumHelper<T> where T : Enum
{
private static readonly ConcurrentDictionary<string, T> Cache = new ConcurrentDictionary<string, T>(StringComparer.OrdinalIgnoreCase);
public static T Parse(string s)
{
return Cache.GetOrAdd(s, k => (T)Enum.Parse(typeof(T), k));
}
}
Usage:
var t = EnumHelper<SearchOption>.Parse(SearchOption.AllDirectories.ToString());
I have a struct which contains two public variables. I have made an array of that struct, and wish to convert it to a Dictionary.
Here is one such method of accomplishing that:
public class TestClass
{
public struct KeyValuePairs
{
public string variableOne;
public float variableTwo
}
private KeyValuePairs[] keyValuePairs;
private Dictionary<string, float> KeyValuePairsToDictionary()
{
Dictionary<string, float> dictionary = new Dictionary<string, float>();
for(int i = 0; i < keyValuePairs.Length; i++)
{
dictionary.Add(keyValuePairs[i].variableOne, keyValuePairs[i].variableTwo);
}
return dictionary;
}
}
Now, that works for my specific setup, but I wish to try and convert the KeyValuePairsToDictionary() function into a Generic so that it may work across all types.
My first thought, then, was to do something like this:
private Dictionary<T, T> ArrayToDictionary<T>(T[] array)
{
Dictionary<T, T> keyValuePairs = new Dictionary<T, T>();
for(int i = 0; i < array.Length; i++)
{
keyValuePairs.Add(array[i], array[i]); //The problem is right here.
}
return keyValuePairs;
}
As you can probably tell, I can't access the public fields of whatever struct array I am trying to convert into key-value pairs.
With that, how would you suggest I go about performing the generic conversion?
Please note that my specific setup requires that I convert a struct to a dictionary, for I am using the Unity Game Engine.
Thank you.
A generic way of doing this is already implemented in LINQ.
var dict = myArray.ToDictionary(a => a.TheKey);
With your implementation
public struct KeyValuePairs
{
public string variableOne;
public float variableTwo;
}
and an array
KeyValuePairs[] keyValuePairs = ...;
You get
Dictionary<string, KeyValuePairs> dict = keyValuePairs
.ToDictionary(a => a.variableOne);
or alternatively
Dictionary<string, float> dict = keyValuePairs
.ToDictionary(a => a.variableOne, a => a.variableTwo);
Note that the first variant yields a dictionary with values of type KeyValuePairs, while the second one yields values of type float.
According to the conversation, it seems that you are interested on how you would implement this. Here is a suggestion:
public static Dictionary<TKey, TValue> ToDictionary<T, TKey, TValue>(
this IEnumerable<T> source,
Func<T, TKey> getKey,
Func<T, TValue> getValue)
{
var dict = new Dictionary<TKey, TValue>();
foreach (T item in source) {
dict.Add(getKey(item), getValue(item));
}
return dict;
}
Or simply like this, if you want to store the item itself as value
public static Dictionary<TKey, T> ToDictionary<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> getKey
{
var dict = new Dictionary<TKey, T>();
foreach (T item in source) {
dict.Add(getKey(item), item);
}
return dict;
}
You can use Reflection to achieve that
First of all, add a {get;set;} to the variables to transform them in properties
public struct KeyValuePairs
{
public string variableOne { get; set; }
public float variableTwo { get; set; }
}
Then the method
// T1 -> Type of variableOne
// T2 -> Type of variableTwo
// T3 -> KeyValuesPair type
public static Dictionary<T1, T2> convert<T1,T2,T3>(T3[] data)
{
// Instantiate dictionary to return
Dictionary<T1, T2> dict = new Dictionary<T1, T2>();
// Run through array
for (var i = 0;i < data.Length;i++)
{
// Get 'key' value via Reflection to variableOne
var key = data[i].GetType().GetProperty("variableOne").GetValue(data[i], null);
// Get 'value' value via Reflection to variableTow
var value = data[i].GetType().GetProperty("variableTwo").GetValue(data[i], null);
// Add 'key' and 'value' to dictionary casting to properly type
dict.Add((T1)key, (T2)value);
}
//return dictionary
return dict;
}
I used the following code to test
KeyValuePairs[] val = new KeyValuePairs[5];
val[0] = new KeyValuePairs() { variableOne = "a", variableTwo = 2.4f };
val[1] = new KeyValuePairs() { variableOne = "b", variableTwo = 3.5f };
val[2] = new KeyValuePairs() { variableOne = "c", variableTwo = 4.6f };
val[3] = new KeyValuePairs() { variableOne = "d", variableTwo = 5.7f };
val[4] = new KeyValuePairs() { variableOne = "e", variableTwo = 6.8f };
Dictionary<string, float> dict = convert<string, float,KeyValuePairs>(val);
When I try to run the following code, the foreach statement is throwing the below error at compile time
Cannot convert type 'string' to
'System.Collections.Generic.KeyValuePair>'
namespace myClass
{
public class myDictionary<T>
{
Dictionary<string, List<T>> dictionary = new Dictionary<string, List<T>>();
public void Add(string key, T value)
{
List<T> list;
if (this.dictionary.TryGetValue(key, out list))
{
list.Add(value);
}
else
{
list = new List<T>();
list.Add(value);
this.dictionary[key] = list;
}
}
public IEnumerable<string> Keys
{
get
{
return this.dictionary.Keys;
}
}
public List<T> this[string key]
{
get
{
List<T> list;
if (!this.dictionary.TryGetValue(key, out list))
{
list = new List<T>();
this.dictionary[key] = list;
}
return list;
}
}
public IEnumerator<T> GetEnumerator()
{
return (dictionary as IEnumerable<T>).GetEnumerator();
}
}
class Program
{
static void Main()
{
myDictionary<string> dictionary = new myDictionary<string>();
dictionary.Add("One", "AA");
dictionary.Add("One", "BB");
dictionary.Add("Two", "CC");
dictionary.Add("Two", "DD");
foreach(KeyValuePair<string, List<string>> pair in dictionary)
{
}
}
}
}
Please let me know what is wrong with my implementation. Thanks for your help.
It looks like the problem is:
public IEnumerator<T> GetEnumerator()
{
return (dictionary as IEnumerable<T>).GetEnumerator();
}
However, you'll need to clarify what this should return, since your dictionary is one of lists. Is this meant to be all the values from all the lists in turn? If so, I guess:
public IEnumerator<T> GetEnumerator()
{
return dictionary.Values.SelectMany(x => x).GetEnumerator();
}
If, however, you want to return the pairs, then:
public IEnumerator<KeyValuePair<string, List<T>>> GetEnumerator()
{
return dictionary.GetEnumerator();
}
I'm trying to use a dictionary as a class member. I want to
use a property to get/set the key/value of the dictionary but I'm
confused as how to use a dictionary as a property. Since there are 2
parts, I don't know how to setup the get/sets.
You could try this:
class Example {
private Dictionary<int,string> _map;
public Dictionary<int,string> Map { get { return _map; } }
public Example() { _map = new Dictionary<int,string>(); }
}
Implementation would go along the lines of:
var e = new Example();
e.Map[42] = "The Answer";
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var cl = new cl();
populate(cl.dict);
foreach(var d in cl.dict)
Console.WriteLine(d.Key);
}
private static void populate(Dictionary<int, string> d)
{
for (int i = 0; i < 10 ; i++)
{
if (!d.ContainsKey(i))
{
d.Add(i, i.ToString());
}
}
}
}
public class cl
{
public Dictionary<int, string> dict;
public cl()
{
dict = new Dictionary<int, string>();
}
}
Do you mean this ?
class MyDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> _dictionary;
public void Add(TKey key, TValue value)
{
_dictionary.Add(key, value);
}
public void Clear()
{
_dictionary.Clear();
}
public bool Remve(TKey key)
{
return _dictionary.Remove(key);
}
.... and other methods...
public MyDictionary(Dictionary<TKey, TValue> dictionary)
{
_dictionary = dictionary;
}
}
I need a helper function to convert string like "1=alice;2=bob;3=charlie" into a Dictionary<int, string>, and string like "1=true;2=false;3=true" into a Dictionary<int, bool>, and etc.
To do this, I wrote a lot of helper functions that are basically copy and pasted of each other:
private static void load(Dictionary<int, string> dict, string s)
{
dict.Clear();
string[] items = s.Split(';');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] kv = item.Split('=');
dict[int.Parse(kv[0])] = kv[1];
}
}
}
private static void load(Dictionary<int, bool> dict, string s)
{
dict.Clear();
string[] items = s.Split(';');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] kv = item.Split('=');
dict[int.Parse(kv[0])] = bool.Parse(kv[1]);
}
}
}
private static void load(Dictionary<int, int> dict, string s)
{
dict.Clear();
string[] items = s.Split(';');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] kv = item.Split('=');
dict[int.Parse(kv[0])] = int.Parse(kv[1]);
}
}
}
There are more, on other data types such as long, DateTime, and etc.
Is there a way to have just one helper function? I tried Dictionary<object, object> and it didn't work.
Yes, you should have a generic method instead. Personally I'd make it return a new dictionary rather than clearing an existing one, mind you... and use LINQ to implement it:
private static Dictionary<int, T> Load<T>(string text, Func<string, T> parser)
{
return s.Split(';')
.Where(item => item.Contains("="))
.Select(item => item.Split('=', 2))
.ToDictionary(pair => int.Parse(pair[0]), pair => parser(pair[1]));
}
Then call it with:
Dictionary<int, int> foo = Load(text, int.Parse);
Dictionary<int, bool> bar = Load(text, bool.Parse);
Dictionary<int, string> baz = Load(text, x => x);
You can make it generic and use Convert.ChangeType, which will try to parse any string input to the output type:
private static void load<T>(Dictionary<int, T> dict, string s)
{
dict.Clear();
string[] items = s.Split(';');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] kv = item.Split('=');
dict[int.Parse(kv[0])] = (T)Convert.ChangeType(kv[1], typeof(T));
}
}
}