// Copyright (c) 2014, Yves Goergen, http://unclassified.software/source/collectiondictionary // // Copying and distribution of this file, with or without modification, are permitted provided the // copyright notice and this notice are preserved. This file is offered as-is, without any warranty. using System; using System.Collections.Generic; using System.Linq; namespace Unclassified.Util { /// /// Implements a dictionary that can store multiple values for each key. /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. public class CollectionDictionary : Dictionary> { #region Private data /// /// The empty collection to return in . /// private TValue[] empty = new TValue[0]; #endregion Private data #region Constructors /// /// Initializes a new instance of the class /// that is empty, has the default initial capacity, and uses the default equality comparer /// for the key type. /// public CollectionDictionary() { } /// /// Initializes a new instance of the class /// that contains elements copied from the specified /// and uses the default equality comparer for the key type. /// /// The dictionary whose elements are copied to the new /// . public CollectionDictionary(IDictionary dictionary) { LoadFromDictionary(dictionary); } /// /// Initializes a new instance of the class /// that is empty, has the default initial capacity, and uses the specified /// . /// /// The implementation to use when /// comparing keys, or null to use the default for the /// type of the key. public CollectionDictionary(IEqualityComparer comparer) : base(comparer) { } /// /// Initializes a new instance of the class /// that is empty, has the specified initial capacity, and uses the default equality /// comparer for the key type. /// /// The initial number of elements that the /// can contain. public CollectionDictionary(int capacity) : base(capacity) { } /// /// Initializes a new instance of the class /// that contains elements copied from the specified /// and uses the specified . /// /// The dictionary whose elements are copied to the new /// . /// The implementation to use when /// comparing keys, or null to use the default for the /// type of the key. public CollectionDictionary(IDictionary dictionary, IEqualityComparer comparer) : base(comparer) { LoadFromDictionary(dictionary); } /// /// Initializes a new instance of the class /// that is empty, has the specified initial capacity, and uses the specified /// . /// /// The initial number of elements that the /// can contain. /// The implementation to use when /// comparing keys, or null to use the default for the /// type of the key. public CollectionDictionary(int capacity, IEqualityComparer comparer) : base(capacity, comparer) { } /// /// Initializes the contents from a /// instance. /// /// The dictionary to copy elements from. private void LoadFromDictionary(IDictionary dictionary) { foreach (var kvp in dictionary) { var list = new List(); list.Add(kvp.Value); Add(kvp.Key, list); } } #endregion Constructors #region Data access /// /// Gets the count of all values for all keys. /// public int ValueCount { get { int count = 0; foreach (var kvp in this) { count += kvp.Value.Count; } return count; } } /// /// Adds the specified value to a key in the dictionary. /// /// The key of the element to add the value to. /// The value to add to the key. The value can be null for reference types. public void Add(TKey key, TValue value) { ICollection collection; if (!TryGetValue(key, out collection)) { collection = new List(); base.Add(key, collection); } collection.Add(value); } /// /// Adds multiple values to a key in the dictionary. /// /// The key of the element to add the values to. /// The values to add to the key. public void AddRange(TKey key, IEnumerable values) { ICollection collection; if (!TryGetValue(key, out collection)) { collection = new List(); base.Add(key, collection); } foreach (TValue value in values) { collection.Add(value); } } /// /// Removes the value from the specified key in the dictionary. /// /// The key of the element to remove the value from. /// The value to remove from the key. The value can be null for reference types. public void RemoveValue(TKey key, TValue value) { ICollection collection; if (TryGetValue(key, out collection)) { collection.Remove(value); if (collection.Count == 0) { Remove(key); } } } /// /// Determines whether the dictionary contains a specific value in any key. /// /// The value to locate in the dictionary. The value can be null for reference types. /// true if the dictionary contains the specified value; otherwise, false. public bool ContainsValue(TValue value) { foreach (var kvp in this) { if (kvp.Value.Contains(value)) return true; } return false; } /// /// Returns a collection of values for the specified key. If the key does not exist in the /// dictionary, an empty collection is returned. /// /// The key of the element to return the values from. /// public ICollection GetValuesOrEmpty(TKey key) { ICollection values; if (TryGetValue(key, out values)) { return values; } return empty; } #endregion Data access } }