LINQ to Objects means that we can use LINQ to query objects in a collection. We can access the in-memory data structures using LINQ. We can query any type of object that implements the IEnumerable
interface or IEnumerable<T>
, which is of generic type. Lists, arrays, and dictionaries are some collection objects that can be queried using LINQ. If we don't use LINQ, we have to use the looping method to filter the values in a collection. We have to go through the values one-by-one and then find the required details. However, using LINQ we can directly query collections and filter the required values without using any looping. LINQ provides powerful filtering, ordering, and grouping capabilities that requires minimum coding. For example, if we want to find out the types stored in an assembly and then filter the required details, we can use LINQ to query the assembly details using System.Reflection
classes. The System.Reflection
namespace contains types that retrieve information about assemblies, modules, members, parameters, and other entities as collections are managed code, by examining their metadata. Also, files under a directory are a collection of objects that can be queried using LINQ. We shall see some of the examples for querying some collections.
The following example shows an integer array that contains a set of integers. We can apply the LINQ queries on the array to fetch the required values.
int[] integers = { 1, 6, 2, 27, 10, 33, 12, 8, 14, 5 }; IEnumerable<int> twoDigits = from numbers in integers where numbers >= 10 select numbers; Console.WriteLine("Integers > 10:"); foreach (var number in twoDigits) { Console.WriteLine(number); }
The integers
variable contains an array of integers with different values. The variable twoDigits
, which is of type IEnumerable
, holds the query. To get the actual result, the query has to be executed.
The actual query execution happens when the query variable is iterated through the foreach
loop by calling GetEnumerator()
to enumerate the result. Any variable of type IEnumerable<T>
, can be enumerated using the foreach
construct. Types that support IEnumerable<T>
or a derived interface such as the generic IQueryable<T>
, are called queryable types. All collections such as list, dictionary and other classes are queryable. There are some non-generic IEnumerable
collections like ArrayList
that can also be queried using LINQ. For that, we have to explicitly declare the type of the range variable to the specific type of the objects in the collection, as it is explained in the examples later in this chapter.
The twoDigits
variable will hold the query to fetch the values that are greater than or equal to 10. This is used for fetching the numbers one-by-one from the array. The foreach
loop will execute the query and then loop through the values retrieved from the integer array, and write it to the console. This is an easy way of getting the required values from the collection.
If we want only the first four values from a collection, we can apply the Take()
query operator on the collection object. Following is an example which takes the first four integers from the collection. The four integers in the resultant collection are displayed using the foreach
method.
IEnumerable<int> firstFourNumbers = integers.Take(4); Console.WriteLine("First 4 numbers:"); foreach (var num in firstFourNumbers) { Console.WriteLine(num); }
The opposite of Take()
operator is Skip()
operator, which is used to skip the number of items in the collection and retrieve the rest. The following example skips the first four items in the list and retrieves the remaining.
IEnumerable<int> skipFirstFourNumbers = integers.Skip(4); Console.WriteLine("Skip first 4 numbers:"); foreach (var num in skipFirstFourNumbers) { Console.WriteLine(num); }
This example shows the way to take or skip the specified number of items from the collection. So what if we want to skip or take the items until we find a match in the list? We have operators to get this. They are TakeWhile()
and SkipWhile()
.
For example, the following code shows how to get the list of numbers from the integers
collection until 50 is found. TakeWhile()
uses an expression to include the elements in the collection as long as the condition is true and it ignores the other elements in the list. This expression represents the condition to test the elements in the collection for the match.
int[] integers = { 1, 9, 5, 3, 7, 2, 11, 23, 50, 41, 6, 8 }; IEnumerable<int> takeWhileNumber = integers.TakeWhile(num => num.CompareTo(50) != 0); Console.WriteLine("Take while number equals 50"); foreach (int num in takeWhileNumber) { Console.WriteLine(num.ToString()); }
Similarly, we can skip the items in the collection using SkipWhile()
. It uses an expression to bypass the elements in the collection as long as the condition is true. This expression is used to evaluate the condition for each element in the list. The output of the expression is boolean. If the expression returns false, the remaining elements in the collections are returned and the expression will not be executed for the other elements. The first occurrence of the return value as false will stop the expression for the other elements and returns the remaining elements. These operators will provide better results if used against ordered lists as the expression is ignored for the other elements once the first match is found.
IEnumerable<int> skipWhileNumber = integers.SkipWhile(num => num.CompareTo(50) != 0); Console.WriteLine("Skip while number equals 50"); foreach (int num in skipWhileNumber) { Console.WriteLine(num.ToString()); }
In this section we will see how we can query a custom built objects collection. Let us take the Icecream
object, and build the collection, then we can query the collection. This Icecream
class in the following code contains different properties such as Name, Ingredients, TotalFat
, and Cholesterol
.
public class Icecream { public string Name { get; set; } public string Ingredients { get; set; } public string TotalFat { get; set; } public string Cholesterol { get; set; } public string TotalCarbohydrates { get; set; } public string Protein { get; set; } public double Price { get; set; } }
Now build the Icecreams
list collection using the class defined perviously.
List<Icecream> icecreamsList = new List<Icecream> { new Icecream {Name="Chocolate Fudge Icecream", Ingredients="cream, milk, mono and diglycerides...", Cholesterol="50mg", Protein="4g", TotalCarbohydrates="35g", TotalFat="20g", Price=10.5 }, new Icecream {Name="Vanilla Icecream", Ingredients="vanilla extract, guar gum, cream...", Cholesterol="65mg", Protein="4g", TotalCarbohydrates="26g", TotalFat="16g", Price=9.80 }, new Icecream {Name="Banana Split Icecream", Ingredients="Banana, guar gum, cream...", Cholesterol="58mg", Protein="6g", TotalCarbohydrates="24g", TotalFat="13g", Price=7.5 } };
We have icecreamsList
collection which contains three objects with values of the Icecream
type. Now let us say we have to retrieve all the ice-creams that cost less. We can use a looping method, where we have to look at the price value of each object in the list one-by-one and then retrieve the objects that have less value for the Price
property. Using LINQ, we can avoid looping through all the objects and its properties to find the required ones. We can use LINQ queries to find this out easily. Following is a query that fetches the ice-creams with low prices from the collection. The query uses the where
condition, to do this. This is similar to relational database queries. The query gets executed when the variable of type IEnumerable
is enumerated when referred to in the foreach
loop.
List<Icecream> Icecreams = CreateIcecreamsList(); IEnumerable<Icecream> IcecreamsWithLessPrice = from ice in Icecreams where ice.Price < 10 select ice; Console.WriteLine("Ice Creams with price less than 10:"); foreach (Icecream ice in IcecreamsWithLessPrice) { Console.WriteLine("{0} is {1}", ice.Name, ice.Price); }
As we used List<Icecream>
objects, we can also use ArrayList
to hold the objects, and a LINQ query can be used to retrieve the specific objects from the collection according to our need. For example, following is the code to add the same Icecreams
objects to the ArrayList
, as we did in the previous example.
ArrayList arrListIcecreams = new ArrayList(); arrListIcecreams.Add( new Icecream {Name="Chocolate Fudge Icecream", Ingredients="cream, milk, mono and diglycerides...", Cholesterol="50mg", Protein="4g", TotalCarbohydrates="35g", TotalFat="20g", Price=10.5 }); arrListIcecreams.Add( new Icecream {Name="Vanilla Icecream", Ingredients="vanilla extract, guar gum, cream...", Cholesterol="65mg", Protein="4g", TotalCarbohydrates="26g", TotalFat="16g", Price=9.80 }); arrListIcecreams.Add( new Icecream {Name="Banana Split Icecream", Ingredients="Banana, guar gum, cream...", Cholesterol="58mg", Protein="6g", TotalCarbohydrates="24g", TotalFat="13g", Price=7.5 });
Following is the query to fetch low priced ice-creams from the list.
var queryIcecreanList = from Icecream icecream in arrListIcecreams where icecream.Price < 10 select icecream;
Use the foreach
loop, shown as follows, to display the price of the objects retrieved using the above query.
foreach (Icecream ice in queryIcecreanList) Console.WriteLine("Icecream Price : " + ice.Price);
We all know that a string is a collection of characters. It means that we can directly query a string value. Now let us take a string value and try to find out the number of upper case letters in the string. For example, assign a string value to the variable aString
as shown below.
string aString = "Satheesh Kumar";
Now let us build a query to read the string and find out the number of characters that are in upper case. The query should be of type IEnumerable
.
IEnumerable<char> query = from ch in aString where Char.IsUpper(ch) select ch;
The query uses the Char.IsUpper
method in the where
clause to find out the upper case letters from the string. The following code displays the number of characters that are in upper case:
Console.WriteLine("Count = {0}", count);
A file could be called a collection, irrespective of the data contained in it. Let us create a text file that contains a collection of strings. To get the values from the text file, we can use LINQ queries. Create a text file that contains names of different ice-creams. We can use the StreamReader
object to read each line from the text file. Create a List
object, which is of type string
, to hold the values read from the text file. Once we get the values loaded into the strings List
, we can easily query the list using LINQ queries as we do with normal collection objects. The following sample code reads the text file, and loads the ice-cream names to the string list:
List<string> IcecreamNames = new List<string>(); using( StreamReader sReader = new StreamReader(@"C:Icecreams.txt")) { string str; str = sReader.ReadLine(); while (str != null) { IcecreamNames.Add(str); } }
The following sample code reads the list of strings and retrieves the name of ice-creams in descending order:
IEnumerable<string> icecreamQuery = from name in IcecreamNames orderby name descending select name;
We can verify the result of the query by displaying the names using the following code:
foreach (string nam in icecreamQuery) { Console.WriteLine(nam); }
The following code displays the names and verifies the result of the query:
foreach (string nam in icecreamQuery) { Console.WriteLine(nam); }
Similar to collections used in above examples, the .NET reflection class library can be used to read metadata of the .NET assembly and create the types, type members, parameters, and other properties as collections. These collections support the IEnumerable
interface, which helps us to query using LINQ.
LINQ has lot of standard query operators which can be used for querying different objects that support IEnumerable
interface. We can use all standard query operators, listed in the following table, against objects.
Query Operator type |
Query Operators |
---|---|
Restriction |
Where, OfType |
Projection |
Select, SelectMany |
Joining |
Join, GroupJoin |
Concatenation |
Concat |
Sorting |
OrderBy, OrderByDescending, ThenBy, ThenByDescending, Reverse |
Set |
Distinct, Except, Intersect, Union |
Grouping |
GroupBy |
Conversion |
AsEnumerable, Cast, OfType, ToArray, ToDictionary, ToList, ToLookup |
Equality |
SequenceEqual |
Element |
DefaultIfEmpty, ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault |
Generation |
Empty, Range, Repeat |
Quantifiers |
All, Any, Contains |
Aggregation |
Aggregate, Average, Count, LongCount, Max, Min, Sum |
Partitioning |
Skip, SkipWhile, Take, Takewhile |
In this chapter, we saw some examples to query different objects using LINQ operators. We can use LINQ queries on any object that supports IEnumerable
. By using LINQ, we can avoid using looping methods to loop through the collections and fetch the required details. LINQ provides powerful filtering, ordering, and grouping methods. This will reduce our coding as well as the development time.
3.139.234.48