LINQ – Basics

Linear INtegrated Query (LINQ) is a set of .Net libraries which gives querying capabilities on local and remote data sources. The data sources can be local such as collection, list, xml  or remote such as databases. In this article, I am going to discuss only about LINQ with local data sources.

Before looking into the concepts of LINQ in detail, let us see how LINQ offers simplicity and flexibility through a simple example.

Below is a simple integer array of prime numbers less than 100.

int[] primes = {2,3,5,7,11,

13, 17, 19, 23, 29,

31, 37, 41, 43, 47,

53, 59, 61, 67, 71,

73, 79, 83, 89, 97

};

If I want to get the list of prime numbers less than 10, I can write LINQ query over this array collection as follows

var primesLessThan10 = primes.Where(n => n < 10);

foreach (int n in primesLessThan10)

{

Console.WriteLine(n);

}

Now numbers less than 10 will be printed in console. If we had not used LINQ, we should have written code to loop through all the elements with the conditions (to filter) to arrive at the filtered list.

How LINQ works:

Let us see what happens behind the scenes when the below expression is executed.

primes.Where(n => n < 10)

The above expression calls the Where extension method of IEnumerable interface on primes array with lambda expression (n => n < 10) as its parameter. You might wonder how can we call this Where extension method on integer array, as I have just said Where extension method is available on IEnumerable interface? This is because arrays inherits from IEnumerable interface (Collections, Lists also inherit IEnumerable interface for that matter), so extension method ‘Where’ would also be available for array and can be called upon our integer array primes.

Let us see the below signature of Where extension method.

public static IEnumerable<TSource> Where<TSource>(

this IEnumerable<TSource> source,

Func<TSource, bool> predicate

)

This Where extension method takes Func delegate (Func<TSource, bool> predicate) as parameter and returns a collection of elements which implements IEnumerable interface. The first parameter ‘this IEnumerable<TSource> source’ represents generic enumerable collection (primes array in our case), makes this method an extension method of IEnumerable.

Lambda expression (n => n < 10) is passed as parameter to Where extension method. Lambda expression is nothing but an anonymous function which can be used to create delegate. For those who are not aware of delegate: just like you store a number in int variable, you can store a method in delegate. And when you call the delegate, the method stored gets called.There are lot

In our case,  n <10 is the content of the anonymous function which is passed to Where extension method. For each number in the sequence (primes array), it checks whether the number is less than ten and if it’s true, the element is included in output sequence.

LINQ query operators:

We have seen the Where LINQ query operator above. But there are many query operators (methods) available in LINQ. Let us see few examples.

Order by:

If I want to list the numbers in descending order, I would use the following LINQ query

var primesInDescOrder = primes.OrderByDescending(n => n);

For ascending order, you can use the below query

var primesInAscendingOrder = primes.OrderBy(n => n);

Select:

You can use the following LINQ query to get the square of prime numbers

var primesSquares = primes.Select( n => (n*n) )

Other query operators:

There are many other query operators available in LINQ which makes querying the data hassle free.

Take:

This method returns the specified number of contiguous elements from the beginning

var primesFirst4Elements = primes.Take(4) // Gets the first 4 elements of the array i.e, 2,3,5,7 in our case

Skip:

This method skips the specified number of elements in the sequence and returns the remaining elements.

var primesExceptFirst4Elements = primes.Skip(4);

Min,Max,Count:

Min returns the minimum value in the sequence, Max returns the maximum value in the sequence and Count method returns the number of elements in the sequence.

int min = primes.Min(); //2

int Max = primes.Max(); // 97

int count = primes.Count();  // 25 in our case (number of elements in primes array)

First, Last:

First method will give you the first element of the sequence and last method returns the last element in the sequence

int First = primes.First() // 2

int Last = primes.Last() // 97

For a complete list of query operators, you can see Extension methods section of http://msdn.microsoft.com/en-us/library/9eekhta0(v=vs.110).aspx

Method Chaining:

Let us consider a scenario where you want to get the list of prime numbers between 10 and 20 and square them and print the results (squared) in ascending order.

One option is to build 3 queries as below

var primesBetween10And20 = primes.Where(n => n > 10 && n < 20);

var squares = primesBetween10And20.Select(n => n * n);

var order = squares.OrderByDescending(n => n);

Or you can build a single query by chaining the methods as follows

var primesBetween10And20SquaresInDesc = primes.Where(n => n > 10 && n < 20). Select(n => n * n). OrderByDescending(n => n);

You will get 361, 289,169,121 which are the squares of 19,17,13,11 respectively.

Even though we are using the same variable ‘n’ in all the 3 methods (Where, Select, OrderByDescending) in above query, there is no relation between these 3 variables which are named as ‘n’. This is because these are local variables of anonymous functions ( Lambda) and hence their scopes are different and thus they are different variables.

The below two queries are same.

var primesBetween10And20SquaresInDesc = primes.Where(n => n > 10 && n < 20). Select(n => n * n). OrderByDescending(n => n);

var primesBetween10And20SquaresInDesc = primes.Where(a =>  a> 10 && a < 20).Select(b => b * b).OrderByDescending(c => c);

In the second query, ‘a’ is the variable local to Where method, ‘b’ is the variable local to Select method and ‘c’ is the method local to OrderByDescending method.

In the first query, we are using same local variable name to all the 3 methods. They are different variables as their scopes are different.

Let me show another example to make things clear. I have written 2 foreach loops and I am using the same variable ‘n’ in both. When I run this code, the compiler will not throw any errors as ‘n’ in each loop is local to that foreach loop.

foreach( int n in new int[] {1,2,3,4,5})

{

Console.WriteLine(n);

}

foreach (int n in new int[] { 6, 7, 8, 9, 10 })

{

Console.WriteLine(n);

}

Method chaining will enable you to chain the methods without creating any intermediate variables and thus making building LINQ queries easier and simpler.

Deferred execution:

The important behavior of LINQ that we need to understand is that the LINQ methods are not executed when they are built but when they are enumerated.

Consider the following simple code snippet. I have declared a variable by name factor which I would multiply with each of the prime number and it is initialized with value 2. Then I am building a LINQ query to multiply all the prime number with the value of factor variable. Then I am changing the value of factor to 3 and then I am enumerating the sequence.

int factor = 2;

var primesByFactor = primes.Select(n => n * factor);

factor = 3;

foreach (int n in primesByFactor)

{

Console.WriteLine(n);

}

What do you think, it will be printed primes multiplied by 2 or prime numbers multiplied by 3? Congrats to those who thought that ‘prime numbers multiplied by 3’ will get printed. This is because LINQ queries are executed when they are enumerated not when they are constructed.

How can you make LINQ queries to execute when they are built?

If you want to prevent the default deferred execution behavior, you can use ToList or ToArray method to execute it immediately.

int factor = 2;

var primesByFactor = primes.Select(n => n * factor).ToList();

factor = 3;

foreach (int n in primesByFactor)

{

Console.WriteLine(n);

}

Console.ReadLine();

The above code will print the ‘prime numbers multiplied by 2’ as ToList() method will execute the query.

Query Expressions:

So far we have Fluent Syntax of LINQ while writing queries. There is another popular style of writing queries in LINQ called Query Expressions. LINQ query expressions looks like SQL syntax although there are few differences.

Let us consider the below LINQ query in Fluent syntax

var primesBetween10And20SquaresInDesc = primes.Where(n => n > 10 && n < 20). Select(n => n * n). OrderByDescending(n => n);

I would write the above query in Query expression as follows

var primesBetween10And20SquaresInDescQuery = from n in primes

where (n > 10 && n < 20)

orderby n descending

select (n * n);

This looks like SQL query syntax that we used to query the tables.

If you have followed closely, you might have noticed that there are few differences between the Fluent Syntax and Query Expressions.

  1. In Query Expressions, you need to declare the variable for traversing the sequence before using the same. When you see above Query syntax we have declared variable ‘n’ in first line itself. This variable behaves very much same as a variable in foreach loop.
  2. I have changed the order of methods in Query Expressions a bit. In Fluent syntax, I have called Where,Select,OrderByDescending   methods in the order mentioned. But in Query Expressions, I have used order by clause before select method just like as we write queries in SQL. This is because of Query Expressions always start with from clause and end with either select or group by clause. So I have rearranged the methods being called.

How Query Expressions work:

When we write the Query expressions, the compiler converts our query expressions into fluent syntax only i.e, when it sees a where clause, it replaces with the Where method- the expression of where clause is converted into Lambda expressions and so on.

Some of you might think that if the compiler is going to convert all the query expressions into Fluent syntax, why should not we stick to Fluent syntax all the time?

There is no concrete answer as you can achieve all the things in Fluent syntax itself but there are few things available in Query Expressions which you can take advantage of.

  1. Query Expressions allow you to declare another variable using ‘let’ or ‘into’ clauses along with the traversing variable
  2. Performing join on two datasets can be done in Query Expressions which would look intuitive.

Into keyword:

The ‘into’ keyword allows you to continue with the result of the query just built before.

Let us consider a scenario where I want to select the prime numbers between 10 and 20 and square them and I want to select the squared result only which are greater than 300. I will write a query like below

var primesBetWeen1020Sq = from n in primes

where ( n>10 && n <20)

select n *n

into nsq

where (nsq>300)

select nsq;

In the first 3 lines of the above query, I am selecting the numbers between 10 and 20 and square them and put into variable nsq. Then I am proceeding with filtering of resulted numbers greater than 300. Once you put the temporary result to a variable using into, the context restarts only with the value of nsq. This means that you cannot use variable n after the line of into keyword- using so will result in compiler error.

var primesBetWeen1020Sq1 = from n in primes

where (n > 10 && n < 20)

select n * n

into nsq

where (nsq > 300)

select n; // Will throw compiler error

The above query will throw you the compiler as n is not in context after ‘into nsq’ because when you use into keyword, the context gets restarted only with the knowledge of value that you put in ‘into’ variable.

Let keyword:

Let keyword allows you to use a variable along with the traversing variable. Let us consider the scenario where I want to select the prime numbers whose square is greater than 300. I can write a query like below

var primesSqGreaterThan300 = from n in primes

let selectedn = n * n

where selectedn > 300

select n; // n is still in scope

While using let, the variable created using let is maintained alongside traversing variable. But this was not the case while using into where the context is restarted with only the previous result is carried forward.

Mixing Fluent and Query Expressions in same query:

You can write a LINQ query with both Fluent and Query Expressions.

I want to square the smallest prime number in the array. I can write a query like below.

var primeMinSquare = from n in primes

where n == primes.Min()

select n*n;

The above query is written in Query Expression form with Fluent syntax is used in where clause

Here is the complete code of Program.cs where I have the queries discussed above.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace LINQExample

{

class Program

{

static void Main(string[] args)

{

int[] primes = {2,3,5,7,11,

13, 17, 19, 23, 29,

31, 37, 41, 43, 47,

53, 59, 61, 67, 71,

73, 79, 83, 89, 97

};

var primesLessThan10 = primes.Where(n => n < 10);

Console.WriteLine("List of prime numbers less than 10:");

//Prints 2,3,5,7

foreach (int n in primesLessThan10)

{

Console.WriteLine(n);

}

var primesInDescOrder = primes.OrderByDescending(n => n);

Console.WriteLine("List of prime numbers in descending order:");

//Prints the primes array in Descending order

foreach (int n in primesInDescOrder)

{

Console.WriteLine(n);

}

var primesInAscendingOrder = primes.OrderBy(n => n);

Console.WriteLine("List of prime numbers in Ascending order:");

//Prints the primes array in Ascending order

foreach (int n in primesInAscendingOrder)

{

Console.WriteLine(n);

}

var primesSquares = primes.Select(n => (n * n));

Console.WriteLine("Squares of prime numbers:");

foreach (int n in primesSquares)

{

Console.WriteLine(n);

}

var primesFrom4thElement = primes.Take(4);

Console.WriteLine("First 4 elements");

foreach (int n in primesFrom4thElement)

{

Console.WriteLine(n);

}

var primesExceptFirst4Elements = primes.Skip(4);

Console.WriteLine("From 5th element");

foreach (int n in primesExceptFirst4Elements)

{

Console.WriteLine(n);

}

int min = primes.Min(); //2

int max = primes.Max(); // 97

int count = primes.Count(); // 25

Console.WriteLine("Min:{0},Max:{1},Count:{2}" , min,max,count);

int First = primes.First(); // 2

int Last = primes.Last(); // 97

Console.WriteLine("First:{0}, Last:{1}",First,Last);

var primesBetween10And20SquaresInDesc = primes.Where(a =>  a> 10 && a < 20).Select(b => b * b).OrderByDescending(c => c);

Console.WriteLine("primesBetween10And20SquaresInDesc");

foreach( int n in primesBetween10And20SquaresInDesc)

{

Console.WriteLine(n);

}

foreach( int n in new int[] {1,2,3,4,5})

{

Console.WriteLine(n);

}

foreach (int n in new int[] { 6, 7, 8, 9, 10 })

{

Console.WriteLine(n);

}

//int factor = 2;

//var primesByFactor = primes.Select(n => n * factor);

//factor = 3;

//foreach (int n in primesByFactor)

//{

//    Console.WriteLine(n);

//}

int factor = 2;

var primesByFactor = primes.Select(n => n * factor).ToList();

factor = 3;

foreach (int n in primesByFactor)

{

Console.WriteLine(n);

}

var primesBetween10And20SquaresInDescQuery = from n in primes

where (n > 10 && n < 20)

orderby n descending

select (n * n);

Console.WriteLine("Using Query Syntax");

foreach (int n in primesBetween10And20SquaresInDescQuery)

{

Console.WriteLine(n);

}

var primesBetWeen1020Sq = from n in primes

where ( n>10 && n <20)

select n *n

into nsq

where (nsq>300)

select nsq;

Console.WriteLine("Using into keyword");

foreach (int n in primesBetWeen1020Sq)

{

Console.WriteLine(n);

}

Console.WriteLine("Using Let variable");

var primesSqGreaterThan300 = from n in primes

let selectedn = n * n

where selectedn > 300

select n; // n is still in scope

foreach (int n in primesSqGreaterThan300)

{

Console.WriteLine(n);

}

var primeMinSquare = from n in primes

where n == primes.Min()

select n*n;

foreach (int n in primeMinSquare)

{

Console.WriteLine(n);

}

Console.ReadLine();

}

}

}

P.S : I have discussed only the basics of LINQ here. I want to write about LINQ to XML, but as this article itself has gone past more than 2500 words, I thought I can write about LINQ to XML in separate article.

 

Thanks for reading. If you like this article, please subscribe below so that I can send articles straight to your inbox.

[mc4wp_form]

Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *