Инициализация поиска ‹int, string›

Как объявить новый класс поиска для свойства в подпрограмме инициализатора объекта в c #?

E.g.

new Component() { ID = 1, Name = "MOBO", Category = new Lookup<int, string> } 

Бит категории всегда выдает ошибку компиляции.


У меня есть свойство Category типа Lookup<int, string>, и я хочу создать экземпляр этого свойства через

new Component() { ID = 1, Name = "MOBO", Category = new Lookup<int, string> };

Но я не могу избавиться от ошибок компиляции.


person Hannah    schedule 02.03.2010    source источник
comment
Я не могу понять, что вы здесь пытаетесь сделать ... Может быть, добавить немного больше информации?   -  person Stormenet    schedule 03.03.2010
comment
Так печально, что у Lookups нет конструктора. Они мне нравятся намного больше, чем Dictionary, потому что свойство Key является производным от объекта значения. Гораздо более СУХОЙ, чем словарь, где вам постоянно приходится повторять это снова и снова, Person.SSN - это ключ каждый раз, когда вы добавляете элемент.   -  person AaronLS    schedule 05.12.2012
comment
@AaronLS Думаю, вам понравится KeyedCollection тогда! :) Все, что вам нужно сделать, это унаследовать от него и реализовать метод, который сообщает ему, как получить ключ из значения. (Это метод, который принимает значение в качестве входных данных и возвращает из него ключ.) ... Однако будьте осторожны, он действительно имеет некоторые подводные камни и причуды - я рекомендую прочитать его и сделать несколько тестов перед серьезным использованием.   -  person AnorZaken    schedule 19.09.2015
comment
stackoverflow.com/questions/3850930/multi-value-dictionary   -  person Slai    schedule 26.11.2016


Ответы (6)


Согласно документации MSDN, для класса Lookup не существует общедоступного конструктора: http://msdn.microsoft.com/en-us/library/bb460184.aspx.

Вы можете создать экземпляр Lookup<TKey, TElement>, вызвав ToLookup для объекта, реализующего IEnumerable<T>.

Вам нужно сделать что-то вроде:

new Component { ID = 1, Name = "MOBO", Category = new[] { … }.ToLookup(…) }

Обновите, чтобы ответить на комментарии:

Я не уверен, откуда вы берете информацию о своей категории, поэтому я что-нибудь придумаю ...

new Component {
    ID = 1, 
    Name = "MOBO", 
    Category = new Dictionary<int, string> { 
        { 3, "Beverages" }
        { 5, "Produce" }
    }.ToLookup(o => o.Key, o => o.Value)
}

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

person jrotello    schedule 02.03.2010
comment
Я пробовал это, но не получил intellisense, что должно быть в ... порциях? - person Hannah; 03.03.2010

Из MSDN:

Не существует общедоступного конструктора для создания нового экземпляра Lookup<TKey, TElement>.
Кроме того, Lookup<TKey, TElement> объекты неизменяемы, то есть вы не можете добавлять или удалять элементы или ключи из Lookup<TKey, TElement> объекта после его создания.

person dugas    schedule 02.03.2010

Вот моя попытка. Убедитесь, что ключ неизменяемый (Gist).

public class MultiValueDictionary<TKey, TElement>
: Collection<TElement>, ILookup<TKey, TElement>
{
  public MultiValueDictionary(Func<TElement, TKey> keyForItem)
    : base(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>, IEnumerable<TElement>, IList<TElement>
  {
    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;
    }

    IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
    {
      foreach (var group in base.Items)
        foreach (var item in group)
          yield return item;
    }

    const string IndexError = "Indexing not supported.";
    public int IndexOf(TElement item) => throw new NotSupportedException(IndexError);
    public void Insert(int index, TElement item) => Add(item);
    public bool Contains(TElement item) => Items.Contains(item);
    public void CopyTo(TElement[] array, int arrayIndex) =>
    throw new NotSupportedException(IndexError);
    new IEnumerable<TElement> Items => this;
    public bool IsReadOnly => false;
    TElement IList<TElement>.this[int index]
    {
      get => throw new NotSupportedException(IndexError);
      set => throw new NotSupportedException(IndexError);
    }
  }

  class Grouping : Collection<TElement>, IGrouping<TKey, TElement>
  {
    public Grouping(TKey key) => Key = key;
    public TKey Key { get; }
  }
}
person Shimmy Weitzhandler    schedule 22.08.2017
comment
Как это использовать? keyForIte только для словаря? как использовать без словаря? - person Blackmeser; 01.06.2021

Если по какой-то причине вам просто нужно вернуть пустой ILookup, вы можете вернуть его из пустого словаря. Например, чтобы сделать ILookup<string, int>, вы можете использовать это:

return new Dictionary<string, int>().ToLookup(kvp => kvp.Key, kvp => kvp.Value);

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

person Steve Rukuts    schedule 14.11.2018

Вы не можете просто использовать ToLookup; вы должны рассказать ему, как находить ключи и значения:

// from ChaosPandion's code
using System.Linq; // make sure you have the using statement 

var component = new Component()  
{  
    ID = 1,  
    Name = "MOBO",  
    Category = (Lookup<int, string>)
       (new Dictionary<int, string>() { {1, "one"} })
       .ToLookup(p=>p.Key, p=>p.Value)
}  

Однако я не понимаю, почему вы хотите использовать здесь поиск вместо словаря.

person Gabe    schedule 02.03.2010
comment
Поиск может содержать повторяющиеся ключи и поддерживать порядок. Это сильно отличается от словаря. Кроме того, все неизменное в стороне, поэтому, хотя есть несколько эквивалентных реализаций карты ... это не словарь. - person user2864740; 30.06.2017
comment
@Gabe он хочет использовать отображение поиска от 1 до многих, что невозможно в словаре - person Akshay Anand; 27.08.2017

Поиск работает по той же концепции, что и словари, разница в том, что словари сопоставляют ключ с одним значением, тогда как поисковый запрос сопоставляет ключ со многими значениями.

Это также означает, что:

ILookup<string, Category>

можно рассматривать как:

IDictionary<string, IEnumerable<Category>>

Вы в основном хотели бы использовать ILookup, когда хотите сопоставить множество объектов / значений с одним и тем же ключом. Вы можете создать ILookup из любого списка объектов, в котором вы хотите сгруппировать эти объекты по какому-либо свойству. Видеть:

public class Product
{
    public string Name { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
}

var products = new List<Product>();
products.Add(new Product { Name = "TV", Price = 400, Category = "Electronics" });
products.Add(new Product { Name = "Computer", Price = 900, Category = "Electronics" });
products.Add(new Product { Name = "Keyboard", Price = 50, Category = "Electronics" });
products.Add(new Product { Name = "Orange", Price = 2, Category = "Fruits" });
products.Add(new Product { Name = "Grape", Price = 3, Category = "Fruits" });

// group by category
ILookup<string, Product> lookup = products.ToLookup(prod => prod.Category);

foreach (var item in lookup)
{
    // this first loop would run two times
    // because there are two categories: Electronics and Fruits
    string category = item.Key;
    decimal totalPriceForCategory = item.Sum(i => i.Price);

    foreach (var product in item)
    {
        // for the electronics, this would loop three times
        // for the fruits, this would loop two times
        string name = product.Name;
        decimal price = product.Price;
    }
}

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

IEnumerable<Product> eletronics = lookup["Electronics"];
IEnumerable<Product> fruits = lookup["Fruits"];
person Alisson    schedule 24.08.2018