Tuesday, May 6, 2008

Simple IEnumerable generic operation : Sort, Filter and Map

In .NET 3.5, there is a static class that implement functions to manipulate IEnumerable<T> like sorting, filtering and mapping. You can see that method as orderby, where and select in System.Linq.Enumerable class from assembly of System.Code.dll

I try to find in .NET 2.0 but I cannot find any methods. So I try to use reflector to disassembled System.Core.DLL and look what happen. It still confusing to look at the result from it. So I try to reproduce it by writing code myself in C#.

First I write the delegate generic that is similar to delegate generic of Func<T,TResult> in .NET 3.5.

public delegate B MyFunction<A, B>(A input);



Mapped(select)/Projection


It is simply mapping from source IEnumerable generic to target IEnumerable<T>. The result of IEnumerable <T> target is not the same type as IEnumerable<T>source.The delegate generic MyFunction will specify the mapping mechanism from source to target.



public static IEnumerable<XTarget> Mapped<XSource, XTarget>(IEnumerable<XSource> source,
MyFunction<XSource, XTarget> function)
{
foreach (XSource item in source)
{
yield return function(item);
}
}



Filter(where)


It is simply make a filter from source IEnumerable<T> to target IEnumerable<T>. The result of IEnumerable generic target is the same type as IEnumerable<T>source but the item inside can be different.



public static IEnumerable<XSource> Filter<XSource>(IEnumerable<XSource> source,
MyFunction<XSource, bool> function)
{
foreach (XSource item in source)
{
if (function(item))
{
yield return item;
}

}
}



Sort (orderby)


It simply make a sorting from source IEnumerable<T>to target IEnumerable<T>. It is intended to sort object that have many properties. The properties can be string or valuetype.  For example; I have a collection of user that  has properties of Name and Age.  Name is a type of string and Age is a type of integer.



First we need a class that implement an IComparer<T>.



public class CompareDefinition<X, Y> : IComparer<X> where Y : IComparable<Y>
{
private MyFunction<X,Y> function = null;
private bool descending = false;

/// <summary>
///
/// </summary>
/// <param name="kembali"></param>
public CompareDefinition(MyFunction<X,Y> function):this(function,false)
{
}

/// <summary>
///
/// </summary>
/// <param name="kembali"></param>
/// <param name="descending"></param>
public CompareDefinition(MyFunction<X, Y> function,bool descending)
{
this.function = function;
this.descending = descending;
}

#region IComparer<XSource> Members

/// <summary>
/// Compare methods
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public int Compare(X x, X y)
{
int result = 0;
Y value = function(x);
if (value != null)
{
if (value is string)
{
result = StringComparer.OrdinalIgnoreCase.Compare(value, function(y));
}
else if (value is ValueType)
{
result = value.CompareTo(function(y));
}
else
{
throw new ArgumentException
("The object is not string or valuetype that implements IComparable");
}

}
return this.descending ? -result : result;
}

#endregion
}



This class define how the content to be compared. The X is the object and Y is the property to compare. The method Compare will compare string and any valuetype. If it finds string, than it will use StringComparer to compare the strings. I use OrdinalIgnoreCase compare just in case of quick coding. If it finds, than it will use their own CompareTo methods.



public static IEnumerable<XSource> Sorting<XSource, XKey>(IEnumerable<XSource> source,
MyFunction<XSource, XKey> function, bool IsDsc) where XKey : IComparable<XKey>
{
if (source != null)
{
XSource[] array = (XSource[])source;

Array.Sort(array, new CompareDefinition<XSource, XKey>(function, IsDsc));

foreach (XSource item in array)
{
yield return item;
}
}
}



Method above explain how to sort IEnumerable <T>. For simple use I just cast from IEnumerable<T> to array of T; to use Sort method. The sort method above will sort the array and use my compare definition.



To use the code. I give a small sample.



        static void Main(string[] args)
{
// Get All process from this computer
IEnumerable<Process> procss = Process.GetProcesses();

// Soring descending by process name
IEnumerable<Process> descendingProcess =
Sorting<Process, String>(procss,
delegate(Process p) { return p.ProcessName; }
,
true
);

// filter the process only working set more than 1MB
IEnumerable<Process> filteredProcess =
Filter<Process>(descendingProcess,
delegate(Process p) { return p.WorkingSet64 > (1024 * 1024); });

string processNameTitle = "Process Name";
string memoryUsedTitle = "Memory Used";
string processIDTitle = "Process ID ";
Console.WriteLine("Sorting descending and Filter process only working set more than 1MB");
Console.WriteLine("{0} {1} {2}",
processNameTitle, memoryUsedTitle, processIDTitle);
foreach (Process proc in filteredProcess)
{
Console.WriteLine("{0} {1} {2}",
proc.ProcessName, proc.WorkingSet64,proc.Id);
}
Console.ReadLine();
// show only process id
IEnumerable<int> mapping =
Mapped<Process, int>(filteredProcess,
delegate(Process p) { return p.Id; });

Console.WriteLine("Show Process ID only from above");
foreach (int item in mapping)
{
Console.WriteLine(item);
}
Console.ReadLine();
}





Get all process from this machine assign them to IEnumerable of Process. The result in IEnumerable<Process> will sort descending by process name and produce IEnumerable<Process> too. After that, it will be filtered and produce IEnumerable<Process>. The result will be displayed in console.



IEnumerable<Process> will be mapped into IEnumerable<int>. The result will be displayed in console and only the process ID will be displayed



0 comments: