C#-ILookup 初始化
How do i declare a new lookup class for a property in the object initializer routine in c#?
new Component() { ID = 1, Name = "MOBO", Category = new Lookup<int, string> }
The category bit always get a compile error.
I have a property called Category
that is of the type Lookup<int, string>
and I want to instantiate this property via
new Component() { ID = 1, Name = "MOBO", Category = new Lookup<int, string> };
But I cannot get past the compile errors.
27111 gold badge33 silver badges55 bronze badges
-
I can't really follow what you are trying to do here... Perhaps add a bit more information? – Stormenet Mar 2 '10 at 22:15
-
So sad that Lookups don't have a constructor. I like them so much more than Dictionary's because the Key property is derived from the value object. Far more DRY than a dictionary where you constantly have to tell it again and again the Person.SSN is the key everytime you add an item. – AaronLS Dec 4 '12 at 20:48
-
@AaronLS I guess you'll love KeyedCollection then! :) All you have to do is inherit from it, and implement the method that tells it how to retrieve the key from the value. (Its a method that takes a value as input and returns the key from it.) ...Do be warned however, it does have some pitfalls and quirks - I recommend to read up on it and do some tests before serious use. – AnorZaken Sep 19 '15 at 0:13
-
stackoverflow.com/questions/3850930/multi-value-dictionary – Slai Nov 26 '16 at 6:42
Per MSDN documentation, there is no public constructor for the Lookup
class: http://msdn.microsoft.com/en-us/library/bb460184.aspx
You can create an instance of a Lookup<TKey, TElement>
by calling ToLookup
on an object that implements IEnumerable<T>
.
You will want to do something like:
new Component { ID = 1, Name = "MOBO", Category = new[] { … }.ToLookup(…) }
Update to address comments:
I'm not sure where you are getting your category info from, so I will make something up…
new Component {
ID = 1,
Name = "MOBO",
Category = new Dictionary<int, string> {
{ 3, "Beverages" }
{ 5, "Produce" }
}.ToLookup(o => o.Key, o => o.Value)
}
My guess is that your categories will come from some other source instead of instantiating a dictionary like I did here.
From MSDN:
There is no public constructor to create a new instance of a Lookup<TKey, TElement>
.
Additionally, Lookup<TKey, TElement>
objects are immutable, that is, you cannot add or remove elements or keys from a Lookup<TKey, TElement>
object after it has been created.
Here's my attempt on this. Make sure the key is immutable (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; }
}
}
You can't just use ToLookup; you have to tell it how to find the keys and values:
// 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)
}
I don't understand why you want to use a Lookup here instead of a dictionary, though.
75k1010 gold badges121121 silver badges212212 bronze badges
-
2
Lookups can contain duplicate keys and maintain order. This is distinctly different from a dictionary. Also, the entire immutably thing aside, so while there are some equivalent map implementations .. it's not a Dictionary. – user2864740 Jun 30 '17 at 16:37
-
@Gabe he want to use 1 to Many mapping of lookup which is not possible in Dictionary – Akshay Anand Aug 27 '17 at 1:24
If you just need to return an empty ILookup
for some reason you can return one from an empty dictionary. For example, to make an ILookup<string, int>
, you can use this:
return new Dictionary<string, int>().ToLookup(kvp => kvp.Key, kvp => kvp.Value);
Unfortunately this is the most concise way I can see to do it without having to make a class that implements ILookup
yourself.
Lookups work with the same concept as Dictionaries, the difference is that Dictionaries map a key to a single value, whereas a Lookup map a key to many values.
This also means that:
ILookup<string, Category>
could be seen as:
IDictionary<string, IEnumerable<Category>>
You basically would want to use ILookup
when you want to map many objects/values to a same key. You can build an ILookup
from any list of objects, where you want to group these objects by some property. See:
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;
}
}
You could also get all the products for a category like this:
IEnumerable<Product> eletronics = lookup["Electronics"];
IEnumerable<Product> fruits = lookup["Fruits"];