Is there a better way to mimic Covariance in this example? Ideally I'd like to do:
private IDictionary<string, ICollection<string>> foos;
public IEnumerable<KeyValuePair<string, IEnumerable<string>> Foos
{
get
{
return foos;
}
}
But KeyValuePair<TKey, TValue> is not covariant.
Instead I have to do:
public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Foos
{
get
{
return foos.Select(x =>
new KeyValuePair<string, IEnumerable<string>>(x.Key, x.Value));
}
}
Is there a better/cleaner way?
Unfortunately, KeyValuePair<TKey, TValue> is a struct; and structs don't exhibit variance in .NET.
You can of course solve this by writing your own covariant Pair interface and some simple helpers to convert between sequences of KeyValuePair and your custom Pair interface. This will let you do:
var dict = new Dictionary<string, ICollection<string>>();
var view = dict.GetCovariantView(); // IEnumerable< IPair<string, ICollection<string> > >
// Notice that you can _widen_ both the key and the value types:
var dictView = view.CastPairs<object, IEnumerable<string>>(); // IEnumerable< IPair< object, IEnumerable<String> > >
// The `CastPairs` call is actually unnecessary provided you don't use `var` for the left-hand-side assignment.
// ...this is due to the implicit (and identity-preserving) variant interface conversion in C#, e.g.:
IEnumerable< IPair< Object, IEnumerable<String> > > dictView2 = view;
Console.WriteLine( Object.ReferenceEquals( view, dictView2 ) ); // --> True
Here's some example code that will let you achieve this:
// `out TKey` is for demonstration purposes. In production-quality code you probably should be using invariant key types.
public interface IPair<out TKey, out TValue>
where TKey : notnull
{
TKey Key { get; }
TValue Value { get; }
}
public class Pair<TKey, TValue> : IPair<TKey, TValue>
where TKey : notnull
{
public TKey Key { get; }
public TValue Value { get; }
public Pair(TKey key, TValue value)
{
this.Key = key;
this.Value = value;
}
public Pair(KeyValuePair<TKey, TValue> pair)
: this(pair.Key, pair.Value)
{}
}
public static class PairSequenceExtensions
{
public static IEnumerable<IPair<TKey, TValue>> GetCovariantView<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source)
where TKey : notnull
{
if (source is null) throw new ArgumentNullException(nameof(source));
return source.Select(kvp => new Pair<TKey, TValue>(kvp));
}
public static IEnumerable<IPair<TKey, TValue>> CastPairs<TKey, TValue>(this IEnumerable<IPair<TKey, TValue>> source)
where TKey : notnull
{
if (source is null) throw new ArgumentNullException(nameof(source));
return source;
}
}
Hardly. KVP is a struct: Not an itnerface, is of ValueType.
Interesting SO post on variance.
I think casts are more performant, so I'd prefer to code like this:
private IDictionary<string, IEnumerable<string>> foos;
public IEnumerable<KeyValuePair<string, IEnumerable<string>> Foos
{
get
{
return foos;
}
}
And cast KeyValuePair.Value to ICollection where I really need to. Frankly speaking it depends on how foos is used.
Related
I've been struggling to Google this question as I can't get the wording quite right (hence the title).
The gist is why do one of the below work, is there a shorthand for test3:
var test1 = new Dictionary<string, int>();
test1["Derp"] = 10; // Success
var test2 = new Dictionary<string, List<int>>();
test2["Derp"].Add(10); // Fail
var test3 = new Dictionary<string, List<int>>();
test3["Derp"] = new List<int>();
test3["Derp"].Add(10); // Success
A scenario I'm coming across often is similar to the below (this is a very basic example):
var names = new List<string>() { "Jim", "Fred", "Fred", "Dave", "Jim", "Jim", "Jim" };
var nameCounts = new Dictionary<string, int>();
foreach(var name in names)
{
if (!nameCounts.ContainsKey(name))
nameCounts.Add(name, 0);
nameCounts[name]++;
}
In other words - is there a way to skip the "ContainsKey" check, and go straight to adding to my list (and key automatically)?
Edit: to be clear, I hadn't used the below as in my real-life situation, it isn't quite as simple (unfortunately!)
var nameCounts = names.GroupBy(x => x)
.ToDictionary(x => x.Key, x => x.Count());
Perl calls this auto-vivification, and I use some extensions to Dictionary to implement various forms, you would need the one that uses a lambda to generate the initial values:
//***
// Enhanced Dictionary that auto-creates missing values with seed lambda
// ala auto-vivification in Perl
//***
public class SeedDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
Func<TValue> seedFn;
public SeedDictionary(Func<TValue> pSeedFn) : base() {
seedFn = pSeedFn;
}
public SeedDictionary(Func<TValue> pSeedFn, IDictionary<TKey, TValue> d) : base() {
seedFn = pSeedFn;
foreach (var kvp in d)
Add(kvp.Key, kvp.Value);
}
public new TValue this[TKey key]
{
get
{
if (!TryGetValue(key, out var val))
base[key] = (val = seedFn());
return val;
}
set => base[key] = value;
}
}
So then you could do test2 like so:
var test2 = new SeedDictionary<string, List<int>>(() => new List<int>());
test2["Derp"].Add(10); // works
For your name counts example, you could use the version that auto-creates the default value for the value type:
//***
// Enhanced Dictionary that auto-creates missing values as default
// ala auto-vivification in Perl
//***
public class AutoDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
public AutoDictionary() : base() { }
public AutoDictionary(IDictionary<TKey, TValue> d) : base() {
foreach (var kvp in d)
Add(kvp.Key, kvp.Value);
}
public new TValue this[TKey key]
{
get
{
if (!TryGetValue(key, out var val))
base[key] = val;
return val;
}
set => base[key] = value;
}
}
Another way you can do this (among many), is a little extension method (cutesy of Jon Skeet here)
public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary,TKey key) where TValue : new()
{
TValue ret;
if (!dictionary.TryGetValue(key, out ret))
{
ret = new TValue();
dictionary[key] = ret;
}
return ret;
}
Usage
strong textvar test2 = new Dictionary<string, List<int>>();
var myNewList = test2.GetOrCreate("Derp");
myNewList.Add(10);
// or
var test2 = new Dictionary<string, List<int>>();
test2.GetOrCreate("Derp").Add(10); // winning!
Note : In all my early morning pep, i actually didn't look at this question, Eric Lippert is on the money in the comments, this can be simply done via a GroupBy and a projection to a dictionary with ToDictionary without all the extra fluff of extension methods and classes
Cutesy of Eric Lippert
// Count occurrences of names in a list
var nameCounts = names.GroupBy(x => x)
.ToDictionary(x => x.Key, x => x.Count());
Additional Resources
Enumerable.GroupBy Method
Groups the elements of a sequence.
Enumerable.ToDictionary Method
Creates a Dictionary<TKey,TValue> from an IEnumerable<T>.
I usually do something like this:
TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
where TValue : new()
=> dict.TryGetValue(key, out TValue val) ? val : dict[key] = new TValue();
Edit: Another way is:
TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
where TValue : new()
=> dict.ContainsKey(key) ? dict[key] : dict[key] = new TValue();
I'm not sure if this is as performant, but it works on older C# versions, where my first example doesn't.
Alternative with C# 7 out variable :
foreach(var name in names)
{
nameCounts[name] = nameCounts.TryGetValue(name, out var count) ? count + 1 : 1;
}
I am trying to create a custom ReadOnlyDictionary<TKey, TValue> for .NET 4.0. The approach is to keep a private Dictionary<TKey, TValue> object as well as flags to determine whether adding/removing and item assignment is allowed.
This works fine but I wanted to implement the IDictionary<TKey, TValue> interface for completeness. However, I notice that it extends ICollection<KeyValuePair<TKey, TValue>> whereas none of its properties or methods appear in the Dictionary<TKey, TValue> class. How is this possible? If the interface is implemented, why are ICollection members not exposed?
Furthermore, why does the Dictionary class need to implement ICollection in the first place?
Here is a rough implementation:
public sealed class ReadOnlyDictionary<TKey, TValue>:
//IDictionary<TKey, TValue>,
IEnumerable<KeyValuePair<TKey, TValue>>
{
#region Members.
public bool AllowListEdit { get; private set; }
public bool AllowItemEdit { get; private set; }
private Dictionary<TKey, TValue> Dictionary { get; set; }
#endregion Members.
#region Constructors.
public ReadOnlyDictionary (bool allowListEdit, bool allowItemEdit) { this.AllowListEdit = allowListEdit; this.AllowItemEdit = allowItemEdit; this.Dictionary = new Dictionary<TKey, TValue>(); }
public ReadOnlyDictionary (IEqualityComparer<TKey> comparer, bool allowListEdit, bool allowItemEdit) { this.AllowListEdit = allowListEdit; this.AllowItemEdit = allowItemEdit; this.Dictionary = new Dictionary<TKey, TValue>(comparer); }
public ReadOnlyDictionary (IDictionary<TKey, TValue> dictionary, bool allowListEdit = false, bool allowItemEdit = false) : this(allowListEdit, allowItemEdit) { foreach (var pair in dictionary) { this.Dictionary.Add(pair.Key, pair.Value); } }
public ReadOnlyDictionary (IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer, bool allowListEdit = false, bool allowItemEdit = false) : this(comparer, allowListEdit, allowItemEdit) { foreach (var pair in dictionary) { this.Dictionary.Add(pair.Key, pair.Value); } }
#endregion Constructors.
#region Properties.
public int Count { get { return (this.Dictionary.Count); } }
public IEqualityComparer<TKey> Comparer { get { return (this.Dictionary.Comparer); } }
#endregion Properties.
#region Methods.
private void ThrowItemReadOnlyException () { if (this.AllowListEdit) { throw (new NotSupportedException("This collection does not allow editing items.")); } }
private void ThrowListReadOnlyException () { if (this.AllowItemEdit) { throw (new NotSupportedException("This collection does not allow adding and removing items.")); } }
public bool ContainsValue (TValue value) { return (this.Dictionary.ContainsValue(value)); }
public void Clear () { this.ThrowListReadOnlyException(); this.Dictionary.Clear(); }
#endregion Methods.
#region Interface Implementation: IEnumerable<KeyValuePair<TKey, TValue>>.
IEnumerator IEnumerable.GetEnumerator () { return (this.Dictionary.GetEnumerator()); }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator () { return (this.Dictionary.GetEnumerator()); }
#endregion Interface Implementation: IEnumerable<KeyValuePair<TKey, TValue>>.
#region Interface Implementation: ICollection<KeyValuePair<TKey, TValue>>.
//public int Count { get { return (this.Dictionary.Count); } }
//public bool IsReadOnly { get { return (this.AllowListEdit); } }
//public bool Contains (KeyValuePair<TKey, TValue> item) { throw (new NotImplementedException()); }
//public void Clear () { throw (new NotImplementedException()); }
//public void Add (KeyValuePair<TKey, TValue> item) { throw (new NotImplementedException()); }
//public void CopyTo (KeyValuePair<TKey, TValue> [] array, int arrayIndex) { throw (new NotImplementedException()); }
//public bool Remove (KeyValuePair<TKey, TValue> item) { throw (new NotImplementedException()); }
#endregion Interface Implementation: ICollection<KeyValuePair<TKey, TValue>>.
#region Interface Implementation: IDictionary<TKey, TValue>.
//====================================================================================================
// Interface Implementation: IDictionary<TKey, TValue>.
//====================================================================================================
public Dictionary<TKey, TValue>.KeyCollection Keys { get { return (this.Dictionary.Keys); } }
public Dictionary<TKey, TValue>.ValueCollection Values { get { return (this.Dictionary.Values); } }
public TValue this [TKey key] { get { return (this.Dictionary [key]); } set { this.ThrowItemReadOnlyException(); this.Dictionary [key] = value; } }
public void Add (TKey key, TValue value) { this.ThrowListReadOnlyException(); this.Dictionary.Add(key, value); }
public bool ContainsKey (TKey key) { return (this.Dictionary.ContainsKey(key)); }
public bool Remove (TKey key) { this.ThrowListReadOnlyException(); return (this.Dictionary.Remove(key)); }
public bool TryGetValue (TKey key, out TValue value) { return (this.Dictionary.TryGetValue(key, out value)); }
#endregion Interface Implementation: IDictionary<TKey, TValue>.
}
Dictionary<TKey, TValue> implements the ICollection<KeyValuePair<TKey, TValue>> interface explicitly.
As you can see on the MSDN page for Dictionary, these methods are listed under "Explicit interface implementations".
Explicitly implementing an interface means that those methods will not be available through the concrete type. You'll have to cast the dictionary to an ICollection<T> to be able to call them.
Dictionary<int, int> dict = new Dictionary<int, int>();
bool sync = dict.IsSynchronized; // not allowed
ICollection<KeyValuePair<int, int>> dict = new Dictionary<int, int>();
bool sync = dict.IsSynchronized; // allowed
More on explicit interface implementations: http://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx
Each key is unique in the list. When a new key-value pair arrives, the pair is inserted into the list in the ascending order of value (if key already exists then updates the value).
Please avoid sorting the list for every insertion.
I would suggest SortedDictionary or SortedList
As per MSDN :
SortedList uses less memory than SortedDictionary.
SortedDictionary has faster insertion and removal
operations for unsorted data: O(log n) as opposed to O(n) for
SortedList.
Update : After comments
You will have to order the value by yourself for e.g using a dictioanry
var dictionary = new Dictionary<int, string>{ {1, "Z"}, {2, "A"}};
IOrderedEnumerable<KeyValuePair<int, string>> orderedEnumerable = dictionary.OrderBy(d => d.Value);
You aren't going to get a built in component with this behaviour, it's too non-standard. I'd be looking at why and when I needed these competing behaviours. Effectively you are looking at an alternate key. Short of just writing some for of linked list, off the top of my head, I'd look at SortedList for the by value part of it, and a Dictionary for key.
e.g.
a Dictionary of CustomerID and SortKey and a SortedList of SortKey and value.
I'd try and avoid it if I could on the baiss that maintaining both would cost more than simply returning a list of values in the required order on those occasions when you needed it.
If sorting the items for every enumeration is acceptable, you can use a Dictionary<TKey, TValue> and order the key-value pairs by value when you enumerate it:
var dict = new Dictionary<MyKey, MyValue>();
// insertion (updates value when key already exists)
dict[key] = value;
// enumeration (ordered by value)
foreach (var keyValuePair in dict.OrderBy(kvp => kvp.Value))
{
...
}
I would write an ad-hoc class like the following (not completely tested):
public class DictionarySortedByValue<TKey, TValue> : IDictionary<TKey, TValue>
{
class ValueWrapper : IComparable, IComparable<ValueWrapper>
{
public TKey Key { get; private set; }
public TValue Value { get; private set; }
public ValueWrapper(TKey k, TValue v)
{
this.Key = k;
this.Value = v;
}
public int CompareTo(object obj)
{
if (!(obj is ValueWrapper))
throw new ArgumentException("obj is not a ValueWrapper type object");
return this.CompareTo(obj as ValueWrapper);
}
public int CompareTo(ValueWrapper other)
{
int c = Comparer<TValue>.Default.Compare(this.Value, other.Value);
if (c == 0)
c = Comparer<TKey>.Default.Compare(this.Key, other.Key);
return c;
}
}
private SortedSet<ValueWrapper> orderedElements;
private SortedDictionary<TKey, TValue> innerDict;
public DictionarySortedByValue()
{
this.orderedElements = new SortedSet<ValueWrapper>();
this.innerDict = new SortedDictionary<TKey, TValue>();
}
public void Add(TKey key, TValue value)
{
var wrap = new ValueWrapper(key, value);
this.innerDict.Add(key, value);
this.orderedElements.Add(wrap);
}
public bool ContainsKey(TKey key)
{
return this.innerDict.ContainsKey(key);
}
public ICollection<TKey> Keys
{
get { return this.innerDict.Keys; }
}
public bool Remove(TKey key)
{
TValue val;
if (this.TryGetValue(key, out val))
{
var wrap = new ValueWrapper(key, val);
this.orderedElements.Remove(wrap);
this.innerDict.Remove(key);
return true;
}
return false;
}
public bool TryGetValue(TKey key, out TValue value)
{
return this.innerDict.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get { return this.innerDict.Values; }
}
public TValue this[TKey key]
{
get
{
return this.innerDict[key];
}
set
{
bool removed = this.Remove(key);
this.Add(key, value);
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
this.Add(item.Key, item.Value);
}
public void Clear()
{
this.innerDict.Clear();
this.orderedElements.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
var wrap = new ValueWrapper(item.Key,item.Value);
return this.orderedElements.Contains(wrap);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
this.innerDict.CopyTo(array, arrayIndex);
}
public int Count
{
get { return this.innerDict.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
if (this.Contains(item))
return this.Remove(item.Key);
return false;
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (var el in this.orderedElements)
yield return new KeyValuePair<TKey, TValue>(el.Key, el.Value);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Notes :
it requires that also the TKey type implements IComparable.
the posted code uses only the default Comparer for TKey, and TValue, but
you could pass a custom one through another constructor.
i have a problem with Dictionary, hope you'll help me.
I have the following declaration:
class MainCollection<TKey1, TKey2, TValue> : Dictionary<KeyValuePair<TKey1, TKey2>, TValue>
The problem is that i cant get an element from this dictionary by TKey1 OR TKey2.
Is there a way to get an element only by TKey1 OR TKey2, not TKey1 AND TKey2?
I wrote the following code:
public TValue GetItemByKey1(TKey1 key)
{
MainCollection<int, int, string> Coll = new MainCollection<int, int, string>();
var value = from s in Coll where s.Key.Key == key select s.Value;
}
But it already has two issues:
Compilation error: s.Key.Key == key => operator == can not be applied to types int and TKey1
It looks ugly. Even if compilation would be successful I'm not sure that this is the fastest way to get such items. I guess that Dictionary should something better.
How can i solve such errors? I didnt find any related questions here.
Thanks in advance!
Okay, so you want to be able to lookup by TKey1 or TKey2. Then what you want is three dictionaries, one for each of the keys, and then one for the key-pairs.
class Foo<TFirstKey, TSecondKey, TValue> {
private readonly Dictionary<TFirstKey, List<TValue>> firstDictionary
= new Dictionary<TFirstKey, List<TValue>>();
private readonly Dictionary<TSecondKey, List<TValue>> secondDictionary
= new Dictionary<TSecondKey, List<TValue>>();
private Dictionary<Tuple<TFirstKey, TSecondKey>, TValue> dictionary
= new Dictionary<Tuple<TFirstKey, TSecondKey>, TValue>();
public IEnumerable<TValue> GetByFirstKey(TFirstKey firstKey) {
return this.firstDictionary[firstKey];
}
public IEnumerable<TValue> GetBySecondKey(TSecondKey secondKey) {
return this.secondDictionary[secondKey];
}
public TValue GetByKey(TFirstKey firstKey, TSecondKey secondKey) {
return this.dictionary[Tuple.Create(firstKey, secondKey)];
}
public void Add(TFirstKey firstKey, TSecondKey secondKey, TValue value) {
this.dictionary.Add(Tuple.Create(firstKey, secondKey), value);
if(this.firstDictionary.Keys.Contains(firstKey)) {
this.firstDictionary[firstKey].Add(value);
}
else {
this.firstDictionary.Add(firstKey, new List<TValue> { value });
}
if(this.secondDictionary.Keys.Contains(secondKey)) {
this.secondDictionary[secondKey].Add(value);
}
else {
this.secondDictionary.Add(secondKey, new List<TValue> { value });
}
}
}
Note that only lookup by (TFirstKey, TSecondKey) is unique, so you need GetByFirstKey and GetBySecondKey to return collections.
I'll leave the rest of the details to you.
The point is that if you want fast lookups on either key, you need two dictionaries (one for each coordinate of the key-pair). Using one can be made to work by querying the key set, but that's slow (it's linear to search the keys).
Just add a method to the collection itself:
public TValue GetItemByKey1(TKey1 key)
{
var value = from s in this.Keys where s.Key.Key == key select this[s];
return value.SingleOrDefault();
}
You can have a similar method for TKey2.
Note that these lookups will be much slower than a standard dictionary key lookup, since you're iterating the key collection, rather than taking advantage of the hashtable a dictionary would otherwise use.
I recommend against using KeyValuePair<TKey, TValue> because KVP is a struct and being a key in the dictionary indicates that the object will be around for a while. I would recommend a Tuple<T1, T2> instead. The benefit is that Tuple is a reference type and you can freely pass around without making copies. Also, Tuple is a readonly object just like the KVPair. Here's the way I would write it:
class Program
{
static void Main(string[] args)
{
MainCollection<int, string, DateTime> collection = new MainCollection<int, string, DateTime>();
collection.Add(Tuple<int, string>.Create(1, "Bob"), new DateTime(1992, 12, 1));
collection.Add(Tuple<int, string>.Create(2, "James"), new DateTime(1945, 9, 1));
collection.Add(Tuple<int, string>.Create(3, "Julie"), new DateTime(1976, 7, 15));
DateTime date;
date = collection.GetValue(1);
Console.WriteLine("Bob birthdate: {0}", date);
date = collection.GetValue("Julie");
Console.WriteLine("#3 birthdate: {0}", date);
Console.ReadLine();
}
}
public class MainCollection<TKey1, TKey2, TValue>
{
Tuple<TKey1, TKey2> key;
Dictionary<Tuple<TKey1, TKey2>, TValue> mainCollection = new Dictionary<Tuple<TKey1, TKey2>, TValue>();
public void Add(Tuple<TKey1, TKey2> Key, TValue Value)
{
mainCollection.Add(Key, Value);
}
public TValue GetValue(TKey1 Key)
{
return mainCollection.Where(k => k.Key.Item1.Equals(Key))
.Select(v => v.Value)
.FirstOrDefault();
}
public TValue GetValue(TKey2 Key)
{
return mainCollection.Where(k => k.Key.Item2.Equals(Key))
.Select(v => v.Value)
.FirstOrDefault();
}
}
public class Tuple<T1, T2>
{
readonly T1 item1;
readonly T2 item2;
Tuple(T1 item1, T2 item2)
{
this.item1 = item1;
this.item2 = item2;
}
public static Tuple<T1, T2> Create(T1 Item1, T2 Item2)
{
return new Tuple<T1, T2>(Item1, Item2);
}
public T1 Item1
{ get { return item1; } }
public T2 Item2
{ get { return item2; } }
}
}
NOTE: I included a Tuple implementation in case you are not using .Net 4.0
Update:
Converting the MainCollection object to use multiple dictionaries would look like this:
public class MainCollection<TKey1, TKey2, TValue>
{
Tuple<TKey1, TKey2> key;
Dictionary<TKey1, Tuple<TKey1, TKey2>> k1Dictionary = new Dictionary<TKey1, Tuple<TKey1, TKey2>>();
Dictionary<TKey2, Tuple<TKey1, TKey2>> k2Dictionary = new Dictionary<TKey2, Tuple<TKey1, TKey2>>();
Dictionary<Tuple<TKey1, TKey2>, TValue> mainCollection = new Dictionary<Tuple<TKey1, TKey2>, TValue>();
public void Add(Tuple<TKey1, TKey2> Key, TValue Value)
{
mainCollection.Add(Key, Value);
k1Dictionary.Add(Key.Item1, Key);
k2Dictionary.Add(Key.Item2, Key);
}
public TValue GetValue(TKey1 Key)
{
return mainCollection[k1Dictionary[Key]];
}
public TValue GetValue(TKey2 Key)
{
return mainCollection[k2Dictionary[Key]];
}
}
I need to create a dictionary that has 2 values per key, and it must return one of the 2 values with the same probability.
Example:
myDicry
{
key = "A", value1=15, value2=56;
}
int firstCall = myDicry["A"]; // = 15
int secondCall = myDicry["A"]; // = 56
It would be possible to write an IDictionary<TKey, TValue> implementation that behaved in this manner, but that would not be a good idea: most people would find a non-deterministic indexer for a collection-class very unintuitive.
Instead, I suggest you make this the responsibility of the value for a key, rather than the Dictionary itself. One option would be to write a custom-type that is capable of picking from a set of possibilities with equal probability. For example:
public class UnbiasedRandomPicker<T>
{
private readonly Random _rand = new Random();
private readonly T[] _possibilities;
public UnbiasedRandomPicker(params T[] possibilities)
{
// argument validation omitted
_possibilities = possibilities;
}
public T GetRandomValue()
{
return _possibilities[_rand.Next(_possibilities.Length)];
}
}
You could then use the dictionary like this:
var dict = new Dictionary<string, UnbiasedRandomPicker<int>>
{
{"A", new UnbiasedRandomPicker<int>(15, 56)},
{"B", new UnbiasedRandomPicker<int>(25, 13)}
};
int randomValueFromA = dict["A"].GetRandomValue();
There's nothing built into the framework to do this, but you'd probably want to implement it by creating a "wrapper" type which had a Dictionary<TKey, Tuple<TValue, TValue>>. You'd then write an indexer to choose appropriately between the two values.
I would actually just implement this in a class that uses a Dictionary<TKey, TValue[]> internally. That way you could even implement the type to have a variable number of values per key.
Like:
class RandomDictionary<TKey, TValue>
{
Dictionary<TKey, TValue[]> m_dict;
Random m_random;
public RandomDictionary()
{
m_dict = new Dictionary<TKey, TValue[]>();
m_random = new Random();
}
public TValue this[TKey key]
{
get
{
TValue[] values = m_dict[key];
return values[m_random.Next(0, values.Length)];
}
}
public void Define(TKey key, params TValue[] values)
{
m_dict[key] = new TValue[values.Length];
Array.Copy(values, m_dict[key], values.Length);
}
public bool TryGetValue(TKey key, out TValue value)
{
TValue[] values;
if (!m_dict.TryGetValue(key, out values))
{
value = default(TValue);
return false;
}
value = values[m_random.Next(0, values.Length)];
return true;
}
}
Use Tuple as dictionary value type.
IDictionary<string, Tuple<int, int>> doubleDictionary = new Dictionary<string, Tuple<int, int>>();
// ...
int secondValue = doubleDictionary["A"].Item2;
You could also write an extension method for the dictionary, so you could create something like this:
IDictionary<string, Tuple<int, int>> doubleDictionary = new Dictionary<string, Tuple<int, int>>();
doubleDictionary.GetRandomValueForKey("A");
Then you can use this with any dictionary.
public static void GetRandomValueForKey(this Dictionary<string, Tuple<int, int>> dict,
string key)
{
... Code to return the value
}
^^ that was written off the top of my head, so please excuse me if this is slightly wrong.
This below code will solve the dictionary part of the problem and make the randomization customizable so that you can apply a level so pseudo-randomness that suits your needs. (or simply hard code it instead of the use of a functor)
public class DoubleDictionary<K, T> : IEnumerable<KeyValuePair<K, T>>
{
private readonly Dictionary<K, Tuple<T, T>> _dictionary = new Dictionary<K, Tuple<T, T>>();
private readonly Func<bool> _getFirst;
public DoubleDictionary(Func<bool> GetFirst) {
_getFirst = GetFirst;
}
public void Add(K Key, Tuple<T, T> Value) {
_dictionary.Add(Key, Value);
}
public T this[K index] {
get {
Tuple<T, T> pair = _dictionary[index];
return GetValue(pair);
}
}
private T GetValue(Tuple<T, T> Pair) {
return _getFirst() ? Pair.Item1 : Pair.Item2;
}
public IEnumerable<K> Keys {
get {
return _dictionary.Keys;
}
}
public IEnumerable<T> Values {
get {
foreach (var pair in _dictionary.Values) {
yield return GetValue(pair);
}
}
}
IEnumerator<KeyValuePair<K, T>> IEnumerable<KeyValuePair<K, T>>.GetEnumerator() {
foreach (var pair in _dictionary) {
yield return new KeyValuePair<K, T>(pair.Key, GetValue(pair.Value));
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return ((IEnumerable<KeyValuePair<K, T>>)this).GetEnumerator();
}
}