In my last post about LINQ I shown you an example about how to use the language integrated query to select information about directories. In this post I’ll get more into the syntax and show you something about the functional querying style.
My examples will focus on displaying information about UEFA Champions Leage winners. Thus, I have created a class called Winner that looks this this:
class Winner { string _name; string _country; int _year; public string Name { get { return _name; } set { _name = value; } } public string Countryr { get { return _country; } set { _country = value; } } public int Year { get { return _year; } set { _year = value; } } public Winner(string name, string country, int year) { _name = name; _country = country; _year = year; } };
Also I have created a utility class that returns a list (incomplete) of UCL winners:
class UCL { public static IEnumerable<Winner> GetWinners() { Winner [] winners = { new Winner("Barcelona", "Spain", 2006), new Winner("Liverpool", "England", 2005), new Winner("FC Porto", "Portugal", 2004), new Winner("AC Milan", "Italy", 2003), new Winner("Real Madrid", "Spain", 2002), new Winner("Bayern Munchen", "Germany", 2001), new Winner("Real Madrid", "Spain", 2000), new Winner("Manchester Utd.", "England", 1999), new Winner("Real Madrid", "Spain", 1998), new Winner("Olimpique Marseille", "France", 1993), }; return winners; } };
Now, let’s see how we could display all this info ascending by the year of winning:
IEnumerable<Winner> winners = UCL.GetWinners(); var result = from w in winners orderby w.Year select w; foreach (var w in result) { Console.WriteLine("{0}\t{1}, {2}", w.Year, w.Name, w.Country); }
That lists the following:
1993 Olimpique Marseille, France 1998 Real Madrid, Spain 1999 Manchester Utd., England 2000 Real Madrid, Spain 2001 Bayern Munchen, Germany 2002 Real Madrid, Spain 2003 AC Milan, Italy 2004 FC Porto, Portugal 2005 Liverpool, England 2006 Barcelona, Spain
This SQL-like syntax is however only a “shell” over the functional syntax. It’s just like with the foreach statement. To be able to iterate with foreach, the collection must implement IEnumerable, which has a method that returns a class that implements IEnumerator. What the compiler is doing when using foreach is calling GetEnumerator to get an iterator, and then Reset() on it, and inserts a while(iterator.MoveNext()), using Current to access the current object from the collection. The same happens here with the declarative query syntax.
Now, let’s suppose we want to list only the winners from England. What we have to do is adding a filtering:
var result = from w in winners orderby w.Year where w.Country == "England" select w;
What I wrote above is actually the same with:
var result = winners. OrderBy(w => w.Year). Where(w => w.Country == "England"). Select(w => w);
Here we used the operators OrderBy, Where and Select. These are two of the query operators that allow you to perform filtering, projection and key extraction. These are built of the concept of Lambda expression, which are similar to the CLR delegates. We could rewrite the last query list this:
Func<Winner, bool> filter = w => w.Country == "England"; Func<Winner, int> criteria = w => w.Year; Func<Winner, Winner> project = w => w; var result = winners.OrderBy(criteria).Where(filter).Select(project);
OrderBy and OrderByDescending are operators that impose a partial order over the keys. Operators ThenBy and ThenByDescending are used to apply additional sorting criteria by only on sorted sequences (SortedSequence
Where is used to exclude items from the collection.
Select and SelectMany are operators for projecting only those fields or info that is wanted.
Now, let’s try something more complicated: grouping the winners by country, and inside each group ascending by the winning year. In declarative syntax that would be like this:
var result = from w in winners orderby w.Year group w.Name by w.Country into groups orderby groups.Key select groups; foreach (var w in result) { Console.WriteLine("\n{0}", w.Key); foreach (var e in w) { Console.WriteLine("{0}", e); } }
Enagland Manchester Utd. Liverpool France Olimpique Marseille Germany Bayern Munchen Italy AC Milan Portugal FC Porto Spain Real Madrid Real Madrid Real Madrid Barcelona
In functional programming syntax, the same query is written as:
var result = winners. OrderByDescending(w => w.Year). GroupBy(w => w.Country, w => w.Name). OrderBy(w => w.Key). Select(w => w);
The GroupBy operator imposes a partitioning over a sequence of values based on a key extraction function. It returns a sequence of Grouping values, which contains both the key as well as the group of values mapped to the key. The interface of Grouping is:
public sealed class Grouping<K, T>{ public Grouping(K key, IEnumerable<T> group); public Grouping(); public K Key {get; set;} public IEnumerable<T> Group {get; set;} }
You may wonder why the declarative syntax starts with the from clause. The SQL language has a problem, i.e. the order of clauses is not natural. Select is the first by at the time of selecting, you don’t know where do you select from. The from clause is naturally the first one, and this was rectified in LINQ. You always have to put from as the first clause in a query (when using the declarative syntax). In the functional syntax you can see you have to apply the operators on a collection, a sequence of items (in our case winners). That is the functional equivalent of from.
1 Reply to “LINQ: declarative and functional syntax”