Introduction
LINQ to Objects helps us use LINQ to query objects in a collection. We can access in-memory data structures using LINQ. We can query any type of object that implements the IEnumerable
interface or IEnumerable
, which is of generic type. Lists, arrays, and dictionaries are some collection objects that can be queried using LINQ.
In this article, author N. Satheesh Kumar shows us how to query different objects using LINQ operators and avoid having to use the looping method to filter the values in a collection.
Without LINQ, we would 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 require 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.
Array of Integers
The following example shows an integer array that contains a set of integers. We can apply 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 article.
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 them 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 the 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 a way to take or skip the specified number of items from a collection. So what if we want to skip or take 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 };
IEnmerable<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 a 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 collection 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());
}
Collection of Objects
In this section, we will see how we can query a custom built objects collection. Let us take the Icecream
object, and build a 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 previously.
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 the 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 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 needs. For example, following is the code to add the same Icecreams
objects to an 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 a 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 objects retrieved using the above query:
foreach (Icecream ice in queryIcecreanList)
Console.WriteLine("Icecream Price : " + ice.Price);
Reading from Strings
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);
Reading from Text Files
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 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 names 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 the metadata of a .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 them using LINQ.
Summary
In this article, 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 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.