Hooked on LINQ

Hooked on LINQ - Developers' Wiki
for .NET Language Integrated Query

Companion book for this site
LINQ to Objects Using C# 4.0:
Using and Extending LINQ to Objects and Parallel LINQ (PLINQ)
Quick Search

Advanced Search »

LINQ to Objects - 5 Minute Overview

This Page is locked
Modified: 2010/05/01 21:25 by t_magennis - Categorized as: LINQ to Objects, Samples
I want feedback on this article. Please leave your comments in the discussion page (click on the Discuss button on the top-right of this page)

(See also: Main LINQ to Objects Page)

LINQ to Objects Using C# 4.0: Using and Extending LINQ to Objects and Parallel LINQ (PLINQ) - This is my book, and much of the material initially came from this website and article. Order on the Informit website or Amazon.

LINQ to Objects allows .NET developers to write “queries” over collections of objects. Out of the box there is a large set of query operators that provide a similar depth of functionality to what we expect from any SQL language working with a relational database, and if what we need isn’t present out-of-the-box, we can add our own.

Traditionally, working with collections of objects meant writing a lot of looping code using for loops or foreach loops to iterate through a list carrying out filtering using if statements, and some action like keeping a running sum of a total property. LINQ frees you from having to write looping code; it allows you to write queries that filter a list or calculate aggregate functions on elements in a collection as a set.

We can write queries against any collection type that implements an interface called IEnumerable (and also a new interface called IQueryable, but more on that later). This is almost any collection type built into the .NET class libraries including simple arrays like string[], or int[], and any List<T> collection we define. Let us look at a few of the simplest examples to understand the basic syntax.

int">"> nums = new int[ {0,4,2,6,3,8,3,1};

var result = from n in nums where n < 5 orderby n select n;

foreach(int i in result) Console.WriteLine(i);

Output: 0 1 2 3 3 4

An example of calculating the aggregate sum of all the elements would look like –

int">"> nums = new int[ {0,4,2,6,3,8,3,1};

int result = nums.Sum(); Console.WriteLine(result);

Output: 27

LINQ to Objects extends any type that inherits from IEnumerable (which is almost every collection class in .NET, from simple Arrays to List<T>) to support query operations similar to those available in SQL. We can write queries using any of the built-in Standard Query Operators, or add our own operators if we need to. The standard operators cover a wide variety of categories, at present there are over fifty that form the backbone of LINQ. To get an idea of their scope, here is a list of those operators available to us -

Most of the operators should be familiar if you have ever worked with a relational database writing queries in SQL. One important distinction between writing SQL queries and LINQ queries is that the operator order is reversed. If you are used to Select-From-Where-OrderBy, it might take some time to overcome the muscle memory and move to From-Where-OrderBy-Select.

To demonstrate some of LINQ’s query capabilities let us write a few queries over this data.

First NameLast NameD.O.B.PhoneState
BarneyGottshall19-Oct-1945885 983 8858CA
ArmandoValdes9-Dec-1973848 553 8487WA
AdamGauwain3-Oct-1959115 999 1154AK
JefferyDeane16-Dec-1950677 602 6774CA
CollinZeeman10-Feb-1935603 303 6030FL
StewartKagel20-Feb-1950546 607 5462WA
ChanceLard21-Oct-1951278 918 2789WA
BlaineReifsteck18-May-1946715 920 7157TX
MackKamph17-Sep-1977364 202 3644TX
ArielHazelgrove23-May-1922165 737 1656OR
Sample Contact data

List contacts = Contacts.SampleData();

var q = from c in contacts where c.DateOfBirth.AddYears(35) > DateTime.Now orderby c.DateOfBirth descending select c.FirstName + " " + c.LastName + " b." + c.DateOfBirth.ToString("dd-MMM-yyyy");

foreach(string s in q) Console.WriteLine(s);

Output: Mack Kamph b.17-Sep-1977 Armando Valdes b.09-Dec-1973

The previous example demonstrates how to get a list of contacts who are less than 35 years of age sorted in descending order by age. This query builds a list of formatted strings as the result, but any type can be returned, even an anonymous type (a type we haven’t explicitly defined that holds just our data, but more on that later). Figure 6 demonstrates grouping, which allows you to create a sub-collection of elements based on a value by using the group by construct.

List contacts = Contacts.SampleData();

var q = from c in contacts group c by c.State;

foreach(var group in q) { Console.WriteLine("State: " + group.Key); foreach(Contacts c in group) Console.WriteLine(" {0) {1}", c.FirstName, c.LastName); }

Output: State: CA Barney Gottshall Jeffery Deane State: WA Armando Valdes Stewart Kagel Chance Lard State: AK Adam Gauwain State: FL Collin Zeeman State: TX Blaine Reifsteck Mack Kamph State: OR Ariel Hazelgrove

A key aspect of accessing relational data is the concept of joining. SQL languages have powerful join capabilities to allow queries to be written against normalized data which is a fancy term for not repeating data, by separating data across multiple tables linking by a common value. LINQ allows you to join multiple object collections together using syntax similar to SQL. To demonstrate, in addition to the data shown in Sample Contact data, also consider the following call log data.

NumberDuration (mins)IncomingDateTime
885 983 88582TRUE7-Aug-20068:12
165 737 165615TRUE7-Aug-20069:23
364 202 36441FALSE7-Aug-200610:5
603 303 60302FALSE7-Aug-200610:35
546 607 54624TRUE7-Aug-200611:15
885 983 885815FALSE7-Aug-200613:12
885 983 88583TRUE7-Aug-200613:47
546 607 54621FALSE7-Aug-200620:34
546 607 54623FALSE8-Aug-200610:10
603 303 603023FALSE8-Aug-200610:40
848 553 84873FALSE8-Aug-200614:0
848 553 84877TRUE8-Aug-200614:37
278 918 27896TRUE8-Aug-200615:23
364 202 364420TRUE8-Aug-200617:12
Sample Call Log data

To join the call log data and retrieve the contact name that matches the phone number we would use the following query.

List contacts = Contacts.SampleData(); List callLog = CallLog.SampleData();

var q = from call in callLog join contact in contacts on call.Number equals contact.Phone select new {contact.FirstName, contact.LastName, call.When, call.Duration}; foreach(var call in q) Console.WriteLine(“{0} – {1) {2) ({3}min)”, call.When.ToString("ddMMM HH:m"), call.FirstName, call.LastName, call.Duration);

Output: 07Aug 08:12 - Barney Gottshall (2min) 07Aug 09:23 - Ariel Hazelgrove (15min) 07Aug 10:5 - Mack Kamph (1min) 07Aug 10:35 - Collin Zeeman (2min) 07Aug 11:15 - Stewart Kagel (4min) 07Aug 13:12 - Barney Gottshall (15min) 07Aug 13:47 - Barney Gottshall (3min) 07Aug 20:34 - Stewart Kagel (1min) 08Aug 10:10 - Stewart Kagel (3min) 08Aug 10:40 - Collin Zeeman (23min) 08Aug 14:0 - Armando Valdes (3min) 08Aug 14:37 - Armando Valdes (7min) 08Aug 15:23 - Chance Lard (6min) 08Aug 17:12 - Mack Kamph (20min) This query joins call records with contact data via the phone number.

Going one step further and summarizing data from multiple collections by combining filtering, grouping, joining and aggregate functions all in one query expression, we can demonstrate the power of Query Expressions on in-memory data. The following example show incoming call records from each contact and the aggregate call statistics.

List contacts = Contacts.SampleData(); List callLog = CallLog.SampleData();

var q = from call in callLog where call.Incoming == true group call by call.Number into g join contact in contacts on g.Key equals contact.Phone orderby contact.FirstName, contact.LastName select new { contact.FirstName, contact.LastName, Count = g.Count(), Avg = g.Average( c => c.Duration ), Total = g.Sum( c => c.Duration )}; foreach(var call in q) Console.WriteLine("{0} {1} - Calls:{2}, Time:{3}mins, Avg:{4}mins", call.FirstName, call.LastName, call.Count, call.Total, Math.Round(call.Avg, 2));

Output: Ariel Hazelgrove - Calls:1, Time:15mins, Avg:15mins Armando Valdes - Calls:1, Time:7mins, Avg:7mins Barney Gottshall - Calls:2, Time:5mins, Avg:2.5mins Chance Lard - Calls:1, Time:6mins, Avg:6mins Mack Kamph - Calls:1, Time:20mins, Avg:20mins Stewart Kagel - Calls:1, Time:4mins, Avg:4mins This example shows filtering, ordering, grouping, joining and selection using aggregate values.

(See also: Main LINQ to Objects Page)

LINQ to Object - 5 Minute Overview is Copyright © Troy Magennis.

If you would like to comment on this page, click on the Discuss button located on the top-right of each page. Feel free to edit any mistakes or omissions you find. If you have an objection or find in-appropriate content then contact the administrator. This website is not affiliated with Microsoft®, all content and opinions are those of the specific author and some advice, solutions and article may contain unintentional errors - please use care. Other websites by this author: Focused Objective, Geek Speak Decoded.