Stupid LINQ Tricks

Over a month ago I did a presentation on LINQ and promised a few people I’d share the code from the session. Better late than never, eh?

We warmed up by building our own filtering operator to use in a query. The operator takes an Expression<Predicate<T>>, which we need to compile before we invoking the predicate inside.

public static class MyExtensions
{
    public static IEnumerable<T> Where<T>(
                  this IEnumerable<T> sequence,
                  Expression<Predicate<T>> filter)
    {
        foreach (T item in sequence)
        {
            if (filter.Compile()(item))
            {
                yield return item;
            }
        }
    }
}

The following query uses our custom Where operator:

IEnumerable<Employee> employees = new List<Employee>()
{
    new Employee() { ID= 1, Name="Scott" },
    new Employee() { ID =2, Name="Paul" }
};


Employee scott =
    employees.Where(e => e.Name == "Scott").First();

Of course, if we are just going to compile and invoke the expression there is little advantage to using an Expression<T>, but it generally turns into an “a-ha!” moment when you show someone the difference between an Expression<Predicate<T>> and a plain Predicate<T>. Try it yourself in a debugger.

We also wrote a LINQ version of “Hello, World!” that reads text files from a temp directory (a.txt would contain “Hello,”, while b.txt would contain “World!”. A good demonstration of map-filter-reduce with C# 3.0.

var message = Directory.GetFiles(@"c:\temp\")
                       .Where(fname => fname.EndsWith(".txt"))
                       .Select(fname => File.ReadAllText(fname))
                       .Aggregate(
                           new StringBuilder(),
                          (sb, s) => sb.Append(s).Append(" "),
                          sb => sb.ToString()
                       );


Console.WriteLine(message);

Moving into NDepend territory, we also wrote a query to find the namespaces with the most types (for referenced assemblies only):

var groups = Assembly.GetExecutingAssembly()
         .GetReferencedAssemblies()
         .Select(aname => Assembly.Load(aname))
         .SelectMany(asm => asm.GetExportedTypes())
         .GroupBy(t => t.Namespace)
         .OrderByDescending(g => g.Count())
         .Take(10);

foreach (var group in groups)
{
    Console.WriteLine("{0} {1}", group.Key, group.Count());
    foreach (var type in group)
    {
        Console.WriteLine("\t" + type.Name);
    }
}

And finally, some LINQ to XML code that creates an XML document out of all the executing processes on the machine:

XNamespace ns = "http://odetocode.com/schemas/linqdemo";
XNamespace ext = "http://odetocode.com/schemas/extensions";

XDocument doc =
    new XDocument(
        new XElement(ns + "Processes",
            new XAttribute(XNamespace.Xmlns + "ext", ext),
            from p in Process.GetProcesses()
            select new XElement(ns + "Process",
               new XAttribute("Name", p.ProcessName),
               new XAttribute(ext + "PID", p.Id))));

Followed by a query for the processes ID of any mspaint instances:

var query =
   (from e in doc.Descendants(ns + "Process")
    where (string)e.Attribute("Name") == "mspaint"
    select (string)e.Attribute(ext + "PID"));

More on LINQ to come…

Print | posted @ Tuesday, September 02, 2008 3:13 AM

Comments on this entry:

Gravatar # re: Stupid LINQ Tricks
by Amozabi Kouysk at 9/2/2008 11:37 AM

I noted that Predicate generate a delegate and source code point to it; Expression injects the Expression Tree that represent Lambda Expression.

Is there other consideration?
  
Gravatar # re: Stupid LINQ Tricks
by scott at 9/2/2008 1:56 PM

Amozabi: That's right. Treating code as data (an expression tree) is a powerful concept and enables all the remote LINQ providers to transform C# to SQL, Web Service calls, etc.
  
Gravatar # re: Stupid LINQ Tricks
by James Curran at 9/2/2008 5:12 PM

Shouldn't the lines in Where():
foreach (T item in sequence)
{
if (filter.Compile()(item))

Actually be:
Predicte pred = filter.Compile();
foreach (T item in sequence)
{
if (pred(item))

There's no really sense in recompiling it every time...
  
Gravatar # re: Stupid LINQ Tricks
by scott at 9/2/2008 5:59 PM

James: Yes. Good point!
  
Gravatar # re: Stupid LINQ Tricks
by John S. at 9/3/2008 3:15 PM

Picking nits here, but you can do e.Attribute("Name").Value == "mspaint" instead of casting.
  
Gravatar # re: Stupid LINQ Tricks
by scott at 9/3/2008 3:36 PM

@John:

Good point - I did show both techniques.

The one advantage to casting is when dealing with optional attributes. Casting will yield a null string reference while .Value will throw an exception.
  
Gravatar # re: Stupid LINQ Tricks
by Ross Neilson at 9/5/2008 9:48 PM

This served as a good reminder to me that LINQ isn't just for doing databasey tasks. Providing you give it an IEnumerable it will work on anything.

I was doing some work with reflection today and started off by looping through a load of PropertyInfos. Then I remembered this blog post and trimmed it all down to one line.

Great post Scott, thanks very much.
  

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 2 and 5 and type the answer here: