Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a dictionary like collection that can use a property of its value as the key?

Instead of using Dictionary<TKey,TValue> I want some type of collection class that can use a property of the value as the key, is there something like this?

like image 272
Jeremy Avatar asked Nov 20 '09 23:11

Jeremy


3 Answers

Yes, there is - System.Collections.ObjectModel.KeyedCollection<TKey, TValue>.

That's abstract, and there are no concrete derived classes in the framework as far as I can see, but all you need to implement is GetKeyForItem as far as I can see. For example, you could do this with a delegate:

public class DelegatingKeyedCollection<TKey, TItem> : System.Collections.ObjectModel.KeyedCollection<TKey, TItem>
{
    private readonly Func<TItem, TKey> keySelector;

    public DelegatingKeyedCollection(Func<TItem, TKey> keySelector)
    {
        this.keySelector = keySelector;
    }

    protected override TKey GetKeyForItem(TItem item)
    {
        return keySelector(item);
    }
}
like image 151
Jon Skeet Avatar answered Sep 24 '22 00:09

Jon Skeet


KeyedCollection as Jon Skeet says is the obvious candidate.

A few random remarks about this class:

  • You will of course want the property that you use as the key to be readonly.

  • Its method Contains(TItem item) is inherited from Collection<T>, and is implemented by iterating through the collection. This can therefore be much slower than Contains(TKey key). It's too easy for developers to make the mistake of using the wrong overload, so it may be worth considering implementing your own Contains(TItem item) method:

    public new bool Contains(TItem item)
    {
        if (item == null) throw new ArgumentNullException("item");
        return this.Contains(GetKeyForItem(item));
    }
    
  • Unlike an IDictionary, it doesn't have a method TryGetValue. This can be useful and it might be worth implementing your own:

    public bool TryGetValue(TKey key, out TItem item)
    {
        // If the dictionary exists, use it
        if (Dictionary != null) return Dictionary.TryGetValue(key, out item);
        // Else do it the hard way
        if (!this.Contains(key))
        {
            item = default(TItem);
            return false;
        }
        item = this[key];
        return true;
    }
    
  • It doesn't support enumeration of the keys, which can be useful:

    public IEnumerable<TKey> GetKeys()
    {
        foreach (TItem item in this)
        {
            yield return GetKeyForItem(item);
        }
    }
    
  • Serialization can be inefficient, as it will serialize both its internal list and its internal dictionary. You can get round this if you need to by implementing custom serialization.

like image 24
Joe Avatar answered Sep 22 '22 00:09

Joe


Use a normal one, and when you set the key value pair, specify the property of the value you are interested in.

That was too easy, I must be misunderstanding your request.

Maybe you wanted to use an arbitrary property later rather than at input time. In that case, I think you would have to use multiple dictionary objects (perhaps tied together in a helper class).

like image 39
Karl Avatar answered Sep 22 '22 00:09

Karl