Многозначный словарь? - PullRequest
       0

Многозначный словарь?

19 голосов
/ 03 октября 2010

Кто-нибудь знает о хорошей реализации MultiValueDictionary? По сути, я хочу что-то, что позволяет несколько значений на ключ. Я хочу иметь возможность сделать что-то вроде

dict.Add(key, val);

И если ключ еще не существует, он добавит его, если он это сделает, он просто добавит другое значение к этому ключу. Я просто собираюсь перебрать его, так что мне безразличны другие методы поиска.

Ответы [ 10 ]

35 голосов
/ 21 июня 2014

Microsoft только что добавила официальную предварительную версию того, что вы ищете (называемую MultiDictionary), доступную через NuGet здесь: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

Информацию об использовании и более подробную информацию можно найти в официальном сообщении блога MSDN здесь: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary.aspx

Я разработчик этого пакета, поэтому сообщите мне здесь или на MSDN, если у вас есть какие-либо вопросы по поводу производительности или чего-либо еще.

Надеюсь, это поможет.

20 голосов
/ 03 октября 2010

Вы можете легко сделать один из словаря списков:

public class MultiValueDictionary<Key, Value> : Dictionary<Key, List<Value>> {

  public void Add(Key key, Value value) {
    List<Value> values;
    if (!this.TryGetValue(key, out values)) {
      values = new List<Value>();
      this.Add(key, values);
    }
    values.Add(value);
  }

}
10 голосов
/ 03 октября 2010

Он не существует, но вы можете довольно быстро создать его из словаря и списка:

class MultiDict<TKey, TValue>  // no (collection) base class
{
   private Dictionary<TKey, List<TValue>> _data =  new Dictionary<TKey,List<TValue>>();

   public void Add(TKey k, TValue v)
   {
      // can be a optimized a little with TryGetValue, this is for clarity
      if (_data.ContainsKey(k))
         _data[k].Add(v)
      else
        _data.Add(k, new List<TValue>() { v}) ;
   }

   // more members
}
4 голосов
/ 03 октября 2010

Вот тот, который я недавно написал, который вы можете использовать.

Он имеет класс "MultiValueDictionary", который наследуется от Dictionary.

Он также имеет класс расширения, который позволяет использовать специальную функциональность Add в любом словаре, где типом значения является IList; таким образом, вы не обязаны использовать пользовательский класс, если не хотите.

public class MultiValueDictionary<KeyType, ValueType> : Dictionary<KeyType, List<ValueType>>
{
    /// <summary>
    /// Hide the regular Dictionary Add method
    /// </summary>
    new private void Add(KeyType key, List<ValueType> value)
    {            
        base.Add(key, value);
    }

    /// <summary>
    /// Adds the specified value to the multi value dictionary.
    /// </summary>
    /// <param name="key">The key of the element to add.</param>
    /// <param name="value">The value of the element to add. The value can be null for reference types.</param>
    public void Add(KeyType key, ValueType value)
    {
        //add the value to the dictionary under the key
        MultiValueDictionaryExtensions.Add(this, key, value);
    }
}

public static class MultiValueDictionaryExtensions
{
    /// <summary>
    /// Adds the specified value to the multi value dictionary.
    /// </summary>
    /// <param name="key">The key of the element to add.</param>
    /// <param name="value">The value of the element to add. The value can be null for reference types.</param>
    public static void Add<KeyType, ListType, ValueType>(this Dictionary<KeyType, ListType> thisDictionary, 
                                                         KeyType key, ValueType value)
    where ListType : IList<ValueType>, new()
    {
        //if the dictionary doesn't contain the key, make a new list under the key
        if (!thisDictionary.ContainsKey(key))
        {
            thisDictionary.Add(key, new ListType());
        }

        //add the value to the list at the key index
        thisDictionary[key].Add(value);
    }
}
4 голосов
/ 03 октября 2010

Вы всегда можете использовать Tuple для второго универсального параметра:

var dict = new Dictionary<string,Tuple<string,int,object>>();
dict.Add("key", new Tuple<string,int,object>("string1", 4, new Object()));

Или даже универсальный список в качестве второго универсального параметра:

var dict = new Dictionary<string,List<myType>>();

Это позволит вам связать несколько значений с одним ключом.

Для простоты использования вы можете создать метод расширения, который будет проверять наличие ключа и добавление в список.

2 голосов
/ 28 июля 2014

Просто чтобы добавить свои 0,02 доллара в коллекцию решений:

У меня была такая же потребность еще в 2011 году, и я создал MultiDictionary с педантично полной реализацией всех интерфейсов .NET.Это включает в себя перечислители, которые возвращают стандарт KeyValuePair<K, T> и поддержку свойства IDictionary<K, T>.Values, обеспечивающего набор фактических значений (вместо ICollection<ICollection<T>>).

Таким образом, он аккуратно вписывается в остальную частьклассы .NET коллекции.Я также определил интерфейс IMultiDictionary<K, T> для доступа к операциям, которые являются специфическими для этого вида словаря:

public interface IMultiDictionary<TKey, TValue> :
  IDictionary<TKey, ICollection<TValue>>,
  IDictionary,
  ICollection<KeyValuePair<TKey, TValue>>,
  IEnumerable<KeyValuePair<TKey, TValue>>,
  IEnumerable {

  /// <summary>Adds a value into the dictionary</summary>
  /// <param name="key">Key the value will be stored under</param>
  /// <param name="value">Value that will be stored under the key</param>
  void Add(TKey key, TValue value);

  /// <summary>Determines the number of values stored under a key</summary>
  /// <param name="key">Key whose values will be counted</param>
  /// <returns>The number of values stored under the specified key</returns>
  int CountValues(TKey key);

  /// <summary>
  ///   Removes the item with the specified key and value from the dictionary
  /// </summary>
  /// <param name="key">Key of the item that will be removed</param>
  /// <param name="value">Value of the item that will be removed</param>
  /// <returns>True if the item was found and removed</returns>
  bool Remove(TKey key, TValue value);

  /// <summary>Removes all items of a key from the dictionary</summary>
  /// <param name="key">Key of the items that will be removed</param>
  /// <returns>The number of items that have been removed</returns>
  int RemoveKey(TKey key);

}

Он может быть скомпилирован на любом, начиная с .NET 2.0 и выше, и до сих пор я развертывал его наXbox 360, Windows Phone 7, Linux и Unity 3D.Существует также полный набор модульных тестов, охватывающий каждую строку кода.

Код лицензируется в соответствии с Common Public License (коротко: все идет, но исправления ошибок в коде библиотеки имеютопубликовано) и может быть найдено в моем хранилище Subversion .

2 голосов
/ 04 августа 2011

Вы можете использовать класс MultiDictionary из PowerCollections .

Возвращает ICollection {TValue} для запрашиваемого ключа.

1 голос
/ 23 августа 2017

Все же здесь - моя попытка использовать ILookup<TKey, TElement> и внутренний KeyedCollection. Убедитесь, что свойство ключа является неизменным.
Крест размещен здесь .

public class Lookup<TKey, TElement> : Collection<TElement>, ILookup<TKey, TElement>
{
  public Lookup(Func<TElement, TKey> keyForItem)
    : base((IList<TElement>)new Collection(keyForItem))
  {
  }

  new Collection Items => (Collection)base.Items;

  public IEnumerable<TElement> this[TKey key] => Items[key];
  public bool Contains(TKey key) => Items.Contains(key);
  IEnumerator<IGrouping<TKey, TElement>>
    IEnumerable<IGrouping<TKey, TElement>>.GetEnumerator() => Items.GetEnumerator();

  class Collection : KeyedCollection<TKey, Grouping>
  {
    Func<TElement, TKey> KeyForItem { get; }      
    public Collection(Func<TElement, TKey> keyForItem) => KeyForItem = keyForItem;
    protected override TKey GetKeyForItem(Grouping item) => item.Key;

    public void Add(TElement item)
    {
      var key = KeyForItem(item);
      if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
        collection.Add(item);
      else
        Add(new Grouping(key) { item });
    }

    public bool Remove(TElement item)
    {
      var key = KeyForItem(item);
      if (Dictionary != null && Dictionary.TryGetValue(key, out var collection)
        && collection.Remove(item))
      {
        if (collection.Count == 0)
          Remove(key);
        return true;
      }
      return false;
    }

  }
  class Grouping : Collection<TElement>, IGrouping<TKey, TElement>
  {
    public Grouping(TKey key) => Key = key;
    public TKey Key { get; }
  }
}
0 голосов
/ 23 декабря 2016

Альтернативой пользовательскому типу может быть общее расширение, которое добавляет ключ и значение, когда не найдено:

public static V getValue<K, V>(this IDictionary<K, V> d, K key) where V : new() {
    V v; if (!d.TryGetValue(key, out v)) { v = new V(); d.Add(key, v); } return v; } 

Пример использования:

var d = new Dictionary<int, LinkedList<int>>();
d.getValue(1).AddLast(2);
0 голосов
/ 03 октября 2010

Это должно сделать сейчас ...

public class MultiValueDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
    private Dictionary<TKey, LinkedList<TValue>> _dict = new Dictionary<TKey, LinkedList<TValue>>();

    public void Add(TKey key, TValue value)
    {
        if(!_dict.ContainsKey(key)) _dict[key] = new LinkedList<TValue>();
        _dict[key].AddLast(value);
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        foreach (var list in _dict)
            foreach (var value in list.Value)
                yield return new KeyValuePair<TKey, TValue>(list.Key, value);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...