Why doesn't IGrouping inherit IEnumerable<KeyValuePair> like IDictionary? - c#

IGrouping:
public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>,
IEnumerable
IDictionary:
public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>,
IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
So, IDictionary implements IEnumerable<KeyValuePair<TKey, TValue>> while IGrouping implements IEnumerable<TElement>. If the elements of IGrouping contain keys, why does the interface not also use KeyValuePair? It also seems like methods implemented in IDictionary that would be useful for IGrouping such as IDictionary.ContainsKey are unavailable in IGrouping, meaning any attempt to find a key on a group (in O(1) time) would look something like:
List<int> myList = new List<int>{ 1, 2, 3, 1};
var grp = myList.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());
if (grp.ContainsKey(someValue)){...}
Am I just using IGrouping wrong? What am I missing?

To find if a particular IGrouping<TKey, TValue> contains a particular TKey, just check its Key property directly. No need for a loop.
GroupBy doesn't return IGrouping, it returns IEnumerable<IGrouping<...>>. That is, IGrouping represents the results for one single key value, and you get multiple such results. It cannot return Dictionary<TKey, TValue>, as GroupBy preserves key order, and Dictionary doesn't. No other pre-existing collection type is appropriate here.
Since you don't care about key order, and also don't care about the individual values for each key (since they're identical), you can store your results in a dictionary yourself, like you're doing now. You're doing the right thing.
If you don't need the counts, you can also use a HashSet<int>.
If you end up needing the individual values, you can also use ToLookup.

Related

Are elements in a .NET's Dictionary sequential? [duplicate]

This question already has answers here:
The order of elements in Dictionary
(6 answers)
Closed 9 years ago.
Say, if I create a dictionary like this:
Dictionary<string, MyClass> dic = new Dictionary<string, MyClass>();
dic.add("z1", val1);
dic.add("abc9", val2);
dic.add("abc8", val3);
dic.add("ABC1", val4);
So when I do:
foreach (KeyValuePair<string, MyClass> kvp in dic)
{
}
Am I guaranteed to have these values retrieved as such: "z1", "abc9", "abc8", "ABC1"?
And what if I first do this, will it be: "z1", "abc8", "ABC1"?
dic.Remove("abc9");
No. From MSDN (emphasis mine)
For purposes of enumeration, each item in the dictionary is treated as
a KeyValuePair<TKey, TValue> structure representing a value and its
key. The order in which the items are returned is undefined.
You may want to look at the OrderedDictionary class if you want more control over the iteration order.
The short answer is No. Order is not guaranteed in a Dictionary<TKey, TValue>, nor should you count on order being maintained.
You might want to check into OrderedDictionary instead.
Example:
OrderedDictionary d = new OrderedDictionary();
d.Add("01", "First");
d.Add("02", "Second");
d.Add("03", "Third");
d.Add("04", "Fourth");
d.Add("05", "Fifth");
for(int i = 0; i < d.Count; i++) // Print values in order
{
Console.WriteLine(d[i]);
}
Note there's no generic OrderedDictionary<TKey,TValue> version for some odd reason. However, this question has some hints on how to implement one.
Am I guaranteed to have these values retrieved as such: "z1", "abc9", "abc8", "ABC1"?
Absolutely not. Always treat Dictionary<,> as an unordered collection of key/value pairs. Even though as an implementation detail you'll generally see them in insertion order if you only ever add values, you should not rely on this.
From the documentation:
For purposes of enumeration, each item in the dictionary is treated as a KeyValuePair<TKey, TValue> structure representing a value and its key. The order in which the items are returned is undefined.
(Emphasis mine.)
If you need a particular order, you should use a different collection - potentially in conjunction with a dictionary, if you also need to be able to fetch by key. (It's not entirely uncommon to maintain an IList<TKey> as well as a Dictionary<TKey, TValue> for example.)
No, there is no guarantee of elements order. Also, it can be different depending on actual implementation of IDictionary<...> interface.

Single extension method on IDictionary<K, IEnumerable/IList/ICollection<V>>

I'm trying to write an extension method that will convert IDictionary<K, S<V>> holding any type of collection/sequence (S<V>) to ILookup<K, V> which is more proper data structure in those cases. This means I'd like my extension to work on different S types and interfaces:
IDictionary<K, IEnumerable<V>>
IDictionary<K, ICollection<V>>
IDictionary<K, List<V>>
etc. Ideally, I don't want to write separate implementation for each possible collection type AND I want type inference to do its job.
What I've tried is:
public static ILookup<TKey, TValue>ToLookup<TKey, TCollection, TValue>(
this IDictionary<TKey, TCollection> dictionary)
where TCollection : IEnumerable<TValue>
But it have no TValue in parameters list, so type inference is unable to figure it out - I get "The type arguments for method ToLookup cannot be inferred from the usage".
Is there a chance it could work somehow in other way than adding fake TValue-typed parameter to the method?
Examples of expected usage
I hope all above calls to be possible and result in a call to my single extension method:
var dictOfIEnumerables = new Dictionary<int, IEnumerable<int>>();
var lookupFromIEnumerables = dictOfIEnumerables.ToLookup();
var dictOfICollections = new Dictionary<int, ICollection<int>>();
var lookupFromICollections = dictOfICollections.ToLookup();
var dictOfLists = new Dictionary<int, List<int>>();
var lookupFromLists = dictOfLists.ToLookup();
Because all collections implement IEnumerable<T>, we can just use it instead of the TCollection type paramter. Unfortunately the type inference does not know this. This is the code I wrote:
public static ILookup<TKey, TValue> ToLookup<TKey, TValue>
(this IDictionary<TKey, IEnumerable<TValue>> dict)
{
return dict.SelectMany(p => p.Value.Select
(v => new KeyValuePair<TKey, TValue>(p.Key, v)))
.ToLookup(p => p.Key, p => p.Value);
}
There seems to be no way of making the type inference work, but this method will work if you cast the Dictionary:
((IDictionary<int, IEnumerable<int>>)dictOfLists).ToLookup()
Also you can add Lists to a Dictionary of IEnumerables and cast them back when you need them.
From the bit of testing I've done, here are my results.
If I type dictOfIEnumerables.ToLookup(, I see 4 overloaded methods.
However, if I type dictOfIEnumerables.ToLookup<, I see all 5 overloaded methods.
It appears that type inference isn't working, because of name collision/resolution collision between the ToLookup() that is defined on IEnumerable. Apparently, without the angle brackets, it's resolving to the methods defined on IEnumerable, because that is what TCollection is restricted to. Maybe someone on StackOverflow that is smarter than I can explain to you why it works the way it does.
However, using the specified types does, in fact, work correctly on my machine.

Sorting IDictionary Generic

I need to know if there is any way of ordering an IDictionary without knowing what type it is exactly ...
For example, I have a method that received an object and within this I have a Dictionary object ... all I know is that it is a Dictionary
so I can do this:
public void MyMethod(PropertyInfo propriedadeParametro, object parameters){
IDictionary dictionary = ((IDictionary) propriedadeParametro.GetValue (parameters, null));
}
but need sort the items of this Dictionary by EnumPersonalizado regardless of what the other Type "something?" has
You can't sort a dictionary. A dictionary, by definition, doesn't have an "order" of the items within it. The items are stored in some mechanism that is beyond your control that is designed to make it as efficient as possible for adding, removing, and searching.
The best that you can do is take all of the items out of the dictionary, put them in some other sort of collection, and then sort that.
As to your particular case, it's not clear to us what the type of the key or the value in the dictionary is, and that would need to be known in order to be able to try to sort the data.
see this question.
Dictionaries by themselves don't have an index order. Consider inheriting from the KeyedCollection class instead. It's a merge of a dictionary and an ordinary list, and it's designed to use a member of your items as the key, and have an index order.
There are plenty of legitimate reasons to want to apply a partial ordering to dictionaries based on key, it isn't a fundamental quality that keys be unordered, only that a given key will yield a given value.
That being said, if you find yourself with a non-generic IDictionary, it can actually be quite troublesome to 'sort' by key without knowledge of the key type. In my specific scenario, I wanted a function which would transform an IDictionary into another IDictionary where the entries could be enumerated by the ordered keys.
IDictionary ToSortedDictionary(IDictionary dictionary) {
return new SortedList(dictionary);
}
This will construct a new dictionary instance, such that traversals (foreach) will visit the entries based on the sort order of the keys.
The oddly named SortedList can be found in System.Collections and orders keys using the ÌComparable interface.
IDictionary is IEnumerable, so you can try to do something like new ArrayList(dictionary).Sort(), but it will try to cast members to IComparable, or you can use a Sort overload which accepts an IComparer object.
Another way is to use a reflection - first you find actual Keys/Values types and then you create a call to generic OrderBy.

When SortedDictionary is enumerated does it return KeyValuePairs in expected order?

When I have SortedDictionary<TK, TV> in .NET and I want to enumerate it as ICollection<KeyValuePair<TK, TV>> does it enumerate in expected order?
That is KeyValuePair<TK, TV> with lowest key is returned as first, folloved by KeyValuePair<TK, TV> with second lowest key etc.?
Note: Only answer backed up by reference will be accepted.
From the Reference for GetEnumerator:
"The dictionary is maintained in a sorted order using an internal tree. Every new element is positioned at the correct sort position, and the tree is adjusted to maintain the sort order whenever an element is removed. While enumerating, the sort order is maintained."
Specifically: "While enumerating, the sort order is maintained."
Yes definitely, although you are going to find it very hard to find documentation that clarifies this precisely.
Although the documentation for each of the four GetEnumerator overloads on this type make vague statements about returning "an enumerator that iterates through a collection", it is obvious enough that they should produce equivalent (sorted by key) sequences; remember that a sorted-dictionary is meant to "represent a collection of key/value pairs that are sorted on the key." It would be highly unintuitive and confusing for users if a collection behaved completely differently (i.e. with a different enumeration order) between a foreach loop and a LINQ to Objects query, for example.
The best I can do is provide you with the implementations of the two GetEnumerator methods you appear to be interested in (as of .NET 4.0). They are identical - they return an instance of the nested Enumerator type, with the same arguments for its constructor. The only difference is the boxing of the struct-type in the second overload:
// Used when you do foreach(var kvp in dict) { ... }
public Enumerator<TKey, TValue> GetEnumerator()
{
return new Enumerator<TKey, TValue>
((SortedDictionary<TKey, TValue>) this, 1);
}
// Used when you do:
// foreach(var kvp in (ICollection<KeyValuePair<TKey, TValue>>)dict) { ... }
// or use LINQ to Objects on the collection.
IEnumerator<KeyValuePair<TKey, TValue>>
IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return new Enumerator<TKey, TValue>
((SortedDictionary<TKey, TValue>) this, 1);
}
In fact, the only GetEnumerator overload that has a slightly different implementation is the IDictionary.GetEnumerator method. This changes an argument to the constructor-call such that the resulting enumerator produces DictionaryEntry instances rather than KeyValuePair<,> instances. Of course, the enumeration order will still be the same as with the other overloads.
It depends on the default IComparer implementation of the key, assuming you are not passing one in:
SortedDictionary(Of TKey, TValue) requires a comparer implementation to perform key comparisons. You can specify an implementation of the IComparer(Of T) generic interface by using a constructor that accepts a comparer parameter; if you do not specify an implementation, the default generic comparer Comparer(Of T).Default is used. If type TKey implements the System.IComparable(Of T) generic interface, the default comparer uses that implementation.
Look at the Remarks section of the SortedDictionary<TKey, TValue> page.
So, if your key is a string, the string implementation of IComparable will be used, if an int32 the int32 implementation will be used.

What do nested generics in C# mean?

A bit of a basic question, but one that seems to stump me, nonetheless.
Given a "nested generic":
IEnumerable<KeyValuePair<TKey, TValue>>
Is this stating that IEnumerable can have generic types that are themselves KeyValuePair 's ?
Thanks,
Scott
Yes. The KeyValuePair type expects two generic type parameters. We can either populate them by pointing to concrete types:
IEnumerable<KeyValuePair<string, int>>
Or we can populate them by using other generic parameters already specified by the outer class:
class Dictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
Generic type parameters are always specified "at-use", or at the point where you are using the class or method that requires them. And just like any other parameter, you can fill it with a constant, hard-coded value (or type in this case), or another variable.
Yes, this is "An IEnumerable of Key/Value pairs." It would be declared thusly:
IEnumberable<KeyValuePair<string, string>> reallyComplicatedDictionary =
new IEnumerable<KeyValuePair<string, string>>();
Or similar.
About the only think I can think this particular usage would do is allow you to have a "dictionary" with repeated keys.
In a nut shell, it means that when you enumerate over that IEnumerable, you're going to get KeyValuePair<TKey, TValue> (for whatever types TKey and TValue are set to).
So, yes.
Here
IEnumerable<KeyValuePair<string, int>>
The IEnumerable itself is not a generic. It knows that it is going to contain KeyValuePair. Here KeyValuePair is the generic which can contain any 2 generic types.

Categories

Resources