Introduction
MSDN describes it as “A query syntax that defines a set of query operators that allow traversal, filter and projection operations to be expressed in a direct, declarative way in any .NET framework-based programming language.”
It allows you to query with a strongly typed syntax. LINQ
has its own set of Query operators which make it powerful. These exist in the System.Linq
Namespace within the System.Core
assembly. These are extension methods on the Enumerable and Queryable objects. Below is a table highlighting them. The standard LINQ query operators can be classified into the following types:
Figure 1
There are good numbers of websites that explain these operators with sufficient source code. MSDN, MSDN Magazine, 4guysfromrolla are few of them. This article will focus on a handful of LINQ queries that can improve our C# programming.
LINQ makes the intent of the code more obvious and helps your fellow developers to quickly adapt and work on your code and this is obviously the step after the inline documentation.
-
Avoiding FOREACH on Collections to Filter for Data
You can avoid the
FOREACH
and theIF
clauses inside yourFOREACH
to filter for data. Check the below sample that eliminates the confusing conditional statements with a simpler LINQ query.CLASSIC CODE Users filteredUsers = new Users(); foreach (User currentUser in Users) { If(currentUser.Active && currentUser.Enabled) { If(!currentUser.LoggedIn) filteredUsers.Add(currentUser); } } LINQ CODE var filteredUsers = from currentUser in Users where currentUser.Active && currentUser.AllowLogin&& ! currentUser.LoggedIn select user;
-
Select X Rows
When we have to select a few rows from a collection, we check the loop counter with a fixed constant and break the for loop Or control the loop counter.
CLASSIC CODE Users filteredUsers = new Users(); for(int counter=0; counter < Users.Count; counter++) { If(Users[counter].Active && Users[counter].Enabled) { If(!Users[counter].LoggedIn) filteredUsers.Add(Users[counter]); } If (filteredUsers.Count == 10) break; } LINQ CODE var filteredUsers = Users.Where(u => u.Active && u.AllowLogin && !u.LoggedIn) .Take(10)
-
Take the First Element From a Collection
We face lot of situations where we have to read the first element in a collection. We would start off by evaluating the collection with null and then its elements count and so on. Now, there is a possibility that this collection might be empty. So our classic code would look like:
List<string> adminUsers = RetrieveUsersForAdminRole(RoleType.Admin); User firstUser = null; If (adminUsers != null) && (adminUsers.Count > 0) { firstUser = adminUsers[0]; }
This can be replaced with a simple LINQ Query.
List<string> adminUsers = RetrieveUsersForAdminRole(RoleType.Admin); User firstUser = adminUsers.FirstOrDefault();
If there are no users found, it returns the default value of the underlying type.
-
Let Go the IComparer<T> for Sorting
We no more need to write the tough
IComparer
classes to sort our custom data. We can now use theOrderBy
method to order the data. Look at the sample query below for more brevity.var filteredUsers = Users.Where(u => u.Active && u.AllowLogin && !u.LoggedIn) .OrderBy( u => u.Name);
The ORDERBY function takes in a parameter that is used to sort. If you want do a multi-sort , use the THENBY operator.
var filteredUsers = Users.Where(u => u.Active && u.AllowLogin && !u.LoggedIn) .OrderBy( u => u.Name).ThenBy(u => u.Location);
Yes, you can sort in a Descending fashion too. LINQ provides the ORDERBYDESCENDING operator for this very purpose.
var filteredUsers = Users.Where(u => u.Active && u.AllowLogin && !u.LoggedIn) .OrderByDescending( u => u.Name).ThenBy(u => u.Location);
Note: There is a
ThenByDescending
operator too. -
Do Not Use for Loops to Initialize Arrays
Do not use for loops to initialize arrays. We do this a lot of times to write quick test code. This can be replaced using the
System.Linq.Enuemerable.Range
method.int[] monthsInAYear = new int[12]; for (int counter = 0; counter < monthsInAYear.Length; counter++) { monthsInAYear[counter] = counter + 1; }
Using LinQ it is just one line of code.
int[] monthsInAYearByLINQ = System.Linq.Enumerable.Range(1, 12).ToArray();
-
Replace Two Similar Loops With Concat
If you have to loop through two arrays which are similar in nature, you can use the Concat extension and write concise code.
See the following example.int[] firstArray = System.Linq.Enumerable.Range(1, 12).ToArray(); int[] secondArray = System.Linq.Enumerable.Range(13, 12).ToArray(); foreach (var fa in firstArray) { allElements.AppendLine(string.Format("{0}", fa)); } foreach (var sa in secondArray) { allElements.AppendLine(string.Format("{0}", sa)); }
Now, consider this piece of code which uses the Concat operator.
int[] firstArray = System.Linq.Enumerable.Range(1, 12).ToArray(); int[] secondArray = System.Linq.Enumerable.Range(13, 12).ToArray(); foreach (var a in firstArray.Concat(secondArray)) { allElements.AppendLine(string.Format("{0}", a)); }
-
Avoid Transformations
A transformation is a technique that you follow to return a new collection that is obtained by looping through another collection and applying filters on them. For example, refer the sample below. It creates a new Array after looping through a collection.
public Users[] FindUsers(RoleType r) { IEnumerable<Users> users = FindUsers(); List<Users> filteredUsers = new List<Users>(); foreach (User u in users) { if(u.Role == r) { filteredUsers.Add(new User { FirstName = u.FirstName, … });} } return filteredUsers.ToArray(); }
Instead of creating a temporary list and then filling it up with users, we can use the LINQ SELECT and the
ToArray
Method to return the results for us.public Users[] FindUsers(RoleType r) { return FindUsers() .Select(user => new User { FirstName = u.FirstName, … }); .Where(user => user.Role == r) .ToArray(); }
-
The Let Keyword
The
LET
keyword lets you build temporary variables in your LINQ query, so that your SELECT query becomes easier to read, and maintain. It lets you predefine variables at a certain stage of the query that can be used in the rest of the query.For example, the following query calculates the average twice. This can be replaced by using the
LET
operator. The query will perform faster as the Average is not calculated twice.var results= from store in Stores where store.Sales.Average(s => s.Price) > 500 select new { Name = store.Name, StoreAveragePrice = store. Sales.Average(s => s.Price) };
See the following snippet that uses the
LET
keyword.var results= from store in Stores let AveragePrice = store.Sales.Average(s => s.Price) where AveragePrice > 500 select new { Name = store.Name, StoreAveragePrice = AveragePrice };
-
RegEx + LINQ?
LINQ queries can help you work with Regular Expression results and make your henceforth queries simpler. Look at the following sample that iterates over a collection iterating them over the regex expression.
Note that you can do other LINQ queries on the
RegEx
Matched results.List<string> examinerStatements = new List<string>(); examinerStatements.Add("Mark was present."); examinerStatements.Add("Julie was present."); examinerStatements.Add("John was absent"); System.Text.RegularExpressions.Regex myRegEx = new System.Text.RegularExpressions.Regex("present"); var presentStudents = examinerStatements .Where<string>(statement => myRegEx.IsMatch(statement)) .ToList<string>(); foreach (var examinerStatement in presentStudents) { // }
-
Querying ArrayList
You would have found that you cannot run LINQ queries on an
ArrayList
. That is because it does not implement theSystem.Collections.Generic.IEnumerable<T>
interface.List<string ArrayList myList = new ArrayList(); myList.Add("One"); myList.Add("Two"); myList.Add("Three"); var results = from value in myList select new { value };
You can use the Cast Operator in such cases. The modified code snippet below shows you how to use that.
ArrayList myList = new ArrayList(); myList.Add("One"); myList.Add("Two"); myList.Add("Three"); var results = from value in myList.Cast<string>() select new { value };
I hope these ten samples helped you learn something new and enjoy reading.
References
LINQ: .NET Language-Integrated Query
Related Articles