tags:

views:

148

answers:

6

I need to create a dictionary that has 2 values per key, 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
+7  A: 

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.

Jon Skeet
This is what I was about to suggest but I was beaten to it.
jimplode
@jimplode You can't beat the Skeet
Jouke van der Maas
@Skeet: there is always next time!! ;)
jimplode
A: 

Use Tuple as dictionary value type.

IDictionary<string, Tuple<int, int>> doubleDictionary = new Dictionary<string, Tuple<int, int>>();
// ...
int secondValue = doubleDictionary["A"].Item2;
BlueCode
"When looking up a key, should it always return the first value first, then the second or should it be random?" NO=)
ZAA
Still needs some work to alternate between the values.
Brian Rasmussen
To BlueCode: no good because it must ruturn randomly without .item1 or item2
ZAA
A: 

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.

jimplode
+9  A: 

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();
Ani
thanks, i thinks it's better solution for me!
ZAA
A: 

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();
    }
}
Rune FS
+1  A: 

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;
    }
}
Dan Tao