Friday, November 16, 2007

C# Generic conversion/grouping tricks

Note: none of this is new, it is just new to me.

I used to love C, for its speed and flexibility. I learned to love C# for its exception handling features. To me, the real killer feature of C# 2+ is the generics. It is undeniably easy to understand and makes my work profoundly easier.

One of the problems I have been having with generics is when I need a List<K> when I really need a List<T>. I stumbled upon the generic Converter class (finally) which makes life much easier for these scenarios.

This code builds a collection of floats and uses a delegate to convert it in to a collection of ints
        
public void Testx()
{
List<float> _floats = new List<float>();
_floats.Add(1f);
_floats.Add(11.5f);
_floats.Add(12f);
_floats.Add(1.1f);
_floats.Add(55f);
_floats.Add(124f);
_floats.Add(127.2f);
_floats.Add(6222f);

List<int> _ints = _floats.ConvertAll(ConvertFloatToInt);
}
public static int ConvertFloatToInt(float x)
{
return Convert.ToInt32(x);
}


Cool, but what if it is not a true one-to-one conversion between the source type and the target type? I mean, what if a bunch of Ks convert in to a common T? How about a grouping method?
This code takes in a collection of the source type, a converter, and returns a dictionary of the target type as keys, and collections of the source type as grouped values.

public static IDictionary<T, ICollection<K>> GroupBy<K,T >(ICollection<K> collection, Converter<K, T> converter)
{
Dictionary<T, ICollection<K>> _dictionary = new Dictionary<T, ICollection<K>>();
foreach (K k in collection)
{
T key = converter(k);
if (_dictionary.ContainsKey(key) == false)
{
_dictionary[key] = new List<K>();
}
_dictionary[key].Add(k);
}
return _dictionary;
}
public static int ConvertFloatToMod10Int(float x)
{
return Convert.ToInt32(x % 10);
}
[Test]
public void Testx()
{
List<float> _floats = new List<float>();
_floats.Add(1f);
_floats.Add(11.5f);
_floats.Add(12f);
_floats.Add(1.1f);
_floats.Add(55f);
_floats.Add(124f);
_floats.Add(127.2f);
_floats.Add(6222f);
Converter<float, int> _converter = new Converter<float, int>(ConvertFloatToMod10Int);
IDictionary<int, ICollection<float>> _dictionary = GroupBy(_floats, _converter);
}

Now this generic GroupBy method is going to be very handy in the future, especially since the delegate will dictate the behavior of the converting/grouping functionality.

Lets take this one step further: what if every K converts in to a collection of different Ts? Then we will need a "Fan Out" function (very handy for genetic algorithm processing, it turns out):








public static IDictionary<K, ICollection<T>> FanOut<K, T>(ICollection<K> collection, Converter<K, ICollection<T>> converter)
{
IDictionary<K, ICollection<T>> _dictionary = new Dictionary<K, ICollection<T>>();
foreach (K _k in collection)
{
ICollection<T> fan = converter(_k);
_dictionary.Add(_k,fan);
}
return _dictionary;
}

Labels:

2 Comments:

Anonymous JeroenH said...

Why not use LINQ for this?

var _dictionary = _floats.GroupBy(f => Convert.ToInt32(f % 10)).ToDictionary(g => g.Key);

11:48 PM  
Blogger Peter Weissbrod said...

this was written when linq was still in beta

4:41 AM  

Post a Comment

<< Home