Home

LINQ and the Enumerable Class

   

Introduction to the Enumerable Class

 

Description

In our introduction to LINQ, we saw how to create a LINQ statement by declaring a variable using the var keyword. The var keyword allows you to let the compiler figure out what type of value the statement would produce. To assist you with creating LINQ statements, the .NET Framework provides many interfaces and classes. The interfaces and the classes that implement them are created in the System.Linq namespace defined in the System.Core.dll assembly library.

   

The IEnumerable Interface

Of the classes that provide support for LINQ is the IEnumerable generic interface. In reality, a LINQ statement is an object of type IEnumerable. Based on this, instead of the var keyword, you can declare the statement using that. Inside the <> operator, enter the type of list that the collection variable contains. Here is an example:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    Button btnShow;

    private ColumnHeader colEmployeeNumber;
    private ColumnHeader colFirstName;
    private ColumnHeader colLastName;
    private ColumnHeader colHourlySalary;

    ListView lvwEmployees;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        btnShow = new Button();
        btnShow.Location = new System.Drawing.Point(12, 8);
        btnShow.Width = 75;
        btnShow.Text = "Show";
        btnShow.Click += new System.EventHandler(this.btnShowClick);

        lvwEmployees = new ListView();
        lvwEmployees.Anchor = AnchorStyles.Left | AnchorStyles.Top |
                              AnchorStyles.Right | AnchorStyles.Bottom;
        lvwEmployees.FullRowSelect = true;
        lvwEmployees.GridLines = true;
        lvwEmployees.Location = new Point(12, 40);
        lvwEmployees.Size = new System.Drawing.Size(270, 100);
        lvwEmployees.View = View.Details;

        colEmployeeNumber = new ColumnHeader();
        colEmployeeNumber.Text = "Empl #";
        colEmployeeNumber.Width = 50;
        lvwEmployees.Columns.Add(colEmployeeNumber);

        colFirstName = new ColumnHeader();
        colFirstName.Text = "First Name";
        colFirstName.Width = 80;
        lvwEmployees.Columns.Add(colFirstName);

        colLastName = new ColumnHeader();
        colLastName.Text = "Last Name";
        colLastName.Width = 80;
        lvwEmployees.Columns.Add(colLastName);

        colHourlySalary = new ColumnHeader();
        colHourlySalary.Text = "Salary";
        colHourlySalary.Width = 50;
        colHourlySalary.TextAlign = HorizontalAlignment.Right;
        lvwEmployees.Columns.Add(colHourlySalary);

        Size = new System.Drawing.Size(300, 180);
        Controls.Add(btnShow);
        Controls.Add(lvwEmployees);
        Text = "Employees";
    }

    private void btnShowClick(object sender, EventArgs e)
    {
        var employees = new Employee[]
        {
            new Employee(971974, "Patricia", "Katts", 24.68M),
            new Employee(208411, "Raymond", "Kouma", 20.15M),
            new Employee(279374, "Hél�ne", "Mukoko", 15.55M),
            new Employee(707912, "Bertrand", "Yamaguchi", 24.68M),
            new Employee(971394, "Gertrude", "Monay", 20.55M)
        };

        IEnumerable<Employee> empls = from staffMembers
                                      in employees
                                      select staffMembers;

        foreach (var staff in empls)
        {
            ListViewItem lviCollection = 
            	new ListViewItem(staff.EmployeeNumber.ToString());

            lviCollection.SubItems.Add(staff.FirstName);
            lviCollection.SubItems.Add(staff.LastName);
            lviCollection.SubItems.Add(staff.HourlySalary.ToString());
            lvwEmployees.Items.Add(lviCollection);
        }
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

public class Employee
{
    public int EmployeeNumber;
    public string FirstName;
    public string LastName;
    public decimal HourlySalary;

    public Employee(int number = 0,
                    string firstName = "John",
                    string lastName = "Doe",
                    decimal salary = 0M)
    {
        EmployeeNumber = number;
        FirstName = firstName;
        LastName = lastName;
        HourlySalary = salary;
    }

    internal string GetFullName()
    {
        return LastName + ", " + FirstName;
    }
}

Returning an Enumerable

Instead of processing a query locally, you can hand the job to an external method that would return the query. To create a method that returns a query, specify its return type as IEnumerable. If the method will receive the value it must process, you can pass it as argument. In the body of the method, create a (the) LINQ statement(s) as you see fit. Before the method closes, make sure it returns a value that either is of type IEnumerable or implements that interface. Here is an example:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    private ListBox lbxNumbers;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        lbxNumbers = new ListBox();
        lbxNumbers.Location = new System.Drawing.Point(12, 12);
        lbxNumbers.Width = 100;

        Text = "Numbers";
        MinimizeBox = false;
        MaximizeBox = false;
        Controls.Add(this.lbxNumbers);
        Load += new EventHandler(ExerciseLoad);
        Size = new System.Drawing.Size(130, 145);
        StartPosition = FormStartPosition.CenterScreen;
    }

    IEnumerable GetQuery(double[] list)
    {
        return from n in list select n;
    }

    private void ExerciseLoad(object sender, EventArgs e)
    {
        var numbers = new double[] { 12.44, 525.38, 6.28, 2448.32, 632.04 };

        var number = GetQuery(numbers);

        foreach (var member in number)
            lbxNumbers.Items.Add(member.ToString());
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

In this example, we used a method that returned a list of simple values. In the same way, you can create a method that returns a list of objects. You can also create a method that returns a single value from a query.

Characteristics of Enumerable

 

Introduction

Besides, or instead of, the select statement in LINQ, the IEnumerable interface provides its own mechanism for selecting the values. This can be done using the Select() method that is overloaded with two versions whose syntaxes are:

public static IEnumerable<TResult> Select<TSource, TResult>(
	this IEnumerable<TSource> source,
	Func<TSource, TResult> selector
);
public static IEnumerable<TResult> Select<TSource, TResult>(
	this IEnumerable<TSource> source,
	Func<TSource, int, TResult> selector
);

This method expects a variable that would hold the result of the selection. You can pass it a lambda expression. This can be done as follows:

IEnumerable<Employee> empls = 
	employees.Select<Employee, Employee>(staffMembers => staffMembers);

You don't have to specify the TSource and the TResult classes:

IEnumerable<Employee> empls = 
	employees.Select(staffMembers => staffMembers);

After doing this, the IEnumerable variable can be used in a foreach loop. Here is an example:

IEnumerable<Employee> empls =
	employees.Select(staffMembers => staffMembers);

foreach (var staff in empls)
{
    ListViewItem lviCollection =
    	new ListViewItem(staff.EmployeeNumber.ToString());

    lviCollection.SubItems.Add(staff.FirstName);
    lviCollection.SubItems.Add(staff.LastName);
    lviCollection.SubItems.Add(staff.HourlySalary.ToString());
    lvwEmployees.Items.Add(lviCollection);
}
 

Where is the Enumerable?

To support conditions of a query, the Enumerable class is equipped with a method named Where that is overloaded with two versions. The syntax of one is:

public static IEnumerable Where(this IEnumerable source,
    				Func predicate);

Here is an example of calling it:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    ListBox lbxNumbers;
    ListBox lbxWhere;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        lbxNumbers = new ListBox();
        lbxNumbers.Size = new Size(100, 140);
        lbxNumbers.Location = new System.Drawing.Point(12, 12);

        lbxWhere= new ListBox();
        lbxWhere.Size = new Size(100, 140);
        lbxWhere.Location = new System.Drawing.Point(120, 12);

        Text = "Numbers";
        MinimizeBox = false;
        MaximizeBox = false;
        Controls.Add(lbxNumbers);
        Controls.Add(lbxWhere);

        Load += new EventHandler(ExerciseLoad);
        Size = new System.Drawing.Size(240, 185);
        StartPosition = FormStartPosition.CenterScreen;
    }

    private void ExerciseLoad(object sender, EventArgs e)
    {
        var numbers = new double[]{ 637.58, 48.5, 12.44, 525.38,
                                    45.3, 6.28, 2448.32, 632.04 };

        var lstNumbers = from n
                         in numbers
                         select n;

        foreach (var number in lstNumbers)
            lbxNumbers.Items.Add(number.ToString());

        var lstWhere = (from n
                     in numbers
                     select n).Where(high => high > 500);

        foreach (var where in lstWhere)
            lbxWhere.Items.Add(where.ToString());
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

This would produce:

Numbers

Sorting a List

If you have a simple list of values of a primitive type and the LINQ variable is declared using the var keyword, we saw that you could sort it using the orderby operator. If the list is made of values that each is based on a class, you can also sort it the same way, in the IEnumerable expression. Here is an example:

IEnumerable<Employee> empls = from staffMembers
                              in employees
                              orderby staffMembers.LastName
                              select staffMembers;

To support the ability to sort the values, the IEnumerable interface provides its own OrderBy() method that is overloaded with two versions. Their syntaxes are:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
	this IEnumerable<TSource> source,
	Func<TSource, TKey> keySelector
);
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
	this IEnumerable<TSource> source,
	Func<TSource, TKey> keySelector,
	IComparer<TKey> comparer
);

Here is an example:

IEnumerable<Employee> empls = 
		employees.OrderBy(staffMembers => staffMembers.LastName);

foreach (var staff in empls)
{
    ListViewItem lviCollection = new ListViewItem(staff.EmployeeNumber.ToString());

    lviCollection.SubItems.Add(staff.FirstName);
    lviCollection.SubItems.Add(staff.LastName);
    lviCollection.SubItems.Add(staff.HourlySalary.ToString());
    lvwEmployees.Items.Add(lviCollection);
}

Imagine you want to give the user the ability to sort the list but you want the list to be accessible from sections of the code. One way you can solve this problem is to declare a LINQ variable outside of the code sections but without initializing the variable, as long as you make sure the variable would be initialized before it is used. The problem with the var keyword is that you must initialize its variable when you declare it. The advantage with an IEnumerable variable is that you do not have to initialize it when declaring it. You can declare it, then specify its value in another section of the code.

LINQ and Numbers

 

A Range of Numbers

We know how to create an array of (random) natural numbers and store it in a variable to use a query. Here is an example:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    private ListBox lbxNumbers;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        lbxNumbers = new ListBox();
        lbxNumbers.Size = new Size(100, 140);
        lbxNumbers.Location = new System.Drawing.Point(12, 12);

        Text = "Numbers";
        MinimizeBox = false;
        MaximizeBox = false;
        Controls.Add(this.lbxNumbers);
        Load += new EventHandler(ExerciseLoad);
        Size = new System.Drawing.Size(130, 185);
        StartPosition = FormStartPosition.CenterScreen;
    }

    private void ExerciseLoad(object sender, EventArgs e)
    {
        var numbers = new int[] { 12, 44, 525, 38, 6, 28, 2448, 32, 632, 04 };
        var number = from n in numbers select n;

        foreach (var member in number)
            lbxNumbers.Items.Add(member.ToString());
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

This would produce:

Numbers

In some cases, you may want to work on a consecutive list of numbers such as 1, 2, 3, 4, 5, 6, 7, and 8. Instead of declaring a formal variable, the Enumerable class provides a method named Range that allows you to specify the first number of a range and a count of consecutive numbers to add to create a range. The syntax of the Enumerable.Range() method is:

public static IEnumerable<int> Range(int start, int count);

The first argument passed to this method is the beginning of the range. The second argument specifies how many numbers to add consecutively from the first. To use this method, you can declare a variable of type IEnumerable and assign a call to Enumerable.Range() that receives both arguments. Here is an example:

private void ExerciseLoad(object sender, EventArgs e)
{
    IEnumerable<int> range = Enumerable.Range(22, 8);
    var number = from n in range select n;

    foreach (var member in number)
        lbxNumbers.Items.Add(member.ToString());
}

You can also declare the variable as type var. You would receive the same result:

Range

If you want to restrict the result, you can add a where condition to it. Here is an example:

private void ExerciseLoad(object sender, EventArgs e)
{
    var range = Enumerable.Range(96, 10);
    var number = from n
                 in range
                 where n % 2 == 0
                 select n;

    foreach (var member in number)
        lbxNumbers.Items.Add(member.ToString());
}

Counting the Number of Records

When you create a LINQ statement, it produces a list. Although the list is of type IEnumerable, since this is only an interface, the result relies on an actual class to provide its characteristics. The class that gives you information about a result is called Enumerable.

The Enumerable class in defined in the System.Linq namespace that is part of the System.Core.dll assembly. It is actually the Enumerable class that implements the methods declared in the IEnumerable interface. Because the Enumerable class is extremely big, we cannot review all of its methods. We will use them as we move on and when a particular method becomes necessary.

As mentioned already, a LINQ statement produces an Enumerable list. You can then use that result to access a method of the class. For example, the IEnumerable.Count() method is used to know the number of items in the resulting list. You can access it from the resulting list. Here is an example:

private void btnShowClick(object sender, EventArgs e)
{
    var employees = new Employee[]
    {
            new Employee(971974, "Patricia", "Katts", 24.68M),
            new Employee(208411, "Raymond", "Kouma", 20.15M),
            new Employee(279374, "Hél�ne", "Mukoko", 15.55M),
            new Employee(707912, "Bertrand", "Yamaguchi", 24.68M),
            new Employee(971394, "Gertrude", "Monay", 20.55M)
    };

    IEnumerable empls = from staffMembers
                        in employees
                        orderby staffMembers.LastName
                        select staffMembers;

    foreach (var staff in empls)
    {
            ListViewItem lviCollection = new ListViewItem(staff.EmployeeNumber.ToString());

            lviCollection.SubItems.Add(staff.FirstName);
            lviCollection.SubItems.Add(staff.LastName);
            lviCollection.SubItems.Add(staff.HourlySalary.ToString());
            lvwEmployees.Items.Add(lviCollection);
    }

    txtCount.Text = empls.Count().ToString();
}

This would produce:

Employees

Remember that you can still use the var keyword to declare the variable that would hold the resulting list. The same var keyword can be used for the result of a method call. Here are examples:

IEnumerable empls = from staffMembers
                         in employees
                         orderby staffMembers.LastName
                         select staffMembers;
var total = empls.Count();

Since we have determined that a LINQ statement produces an Enumerable list, if you do not need the list itself, you can declare a variable that is the type returned by a method, put the statement in parentheses, and then access the method outside the closing parenthesis using the period operator. Here is an example:

var total = (from staffMembers
             in employees
             orderby staffMembers.LastName
             select staffMembers).Count();

Remember that the IEnumerable.Count() method returns the number of items in the result of the LINQ statement, not the number of items in the original list. The following examples illustrate it:

private void Exercise_Load(object sender, EventArgs e)
{
    var students = new Student[]
    {
        . . . No Change
    };

    IEnumerable<Student> pupils = from studs
                                  in students
                                  select studs;
    int boys = (from males
                in pupils
                where males.Gender == Genders.Male
                select males).Count();

    int girls = (from females
                 in pupils
                 where ((females.Gender != Genders.Male) && 
			(females.Gender != Genders.Unknown))
                 select females).Count();

    ListViewItem lviStudent = null;
    ListViewGroup grpBoys = new ListViewGroup("Boys");
    ListViewGroup grpGirls = new ListViewGroup("Girls");
    ListViewGroup grpOthers = new ListViewGroup("Others");

    foreach (var std in pupils)
    {
        if (std.Gender == Genders.Male)
        {
            lviStudent = new ListViewItem(std.StudentNumber.ToString(), grpBoys);
        }
        else if (std.Gender == Genders.Female)
        {
            lviStudent = new ListViewItem(std.StudentNumber.ToString(), grpGirls);
        }
        else
        {
            lviStudent = new ListViewItem(std.StudentNumber.ToString(), grpOthers);
        }

        lviStudent.SubItems.Add(std.FirstName);
        lviStudent.SubItems.Add(std.LastName);
        lvwStudents.Items.Add(lviStudent);

        lvwStudents.Groups.Add(grpBoys);
        lvwStudents.Groups.Add(grpGirls);
        lvwStudents.Groups.Add(grpOthers);
    }

    txtBoys.Text = boys.ToString();
    txtGirls.Text = girls.ToString();
}

This would produce:

Students

These different techniques of calling and using the Count() method will be applied to most other methods of the Enumerable class.

An Average of Numbers

If the values you are querying are numbers, you may want to find their average. To assist you, the Enumerable class provides a method named Average that is overloaded with a version for each numeric data type. The syntax for the double type is:

public static double Average(this IEnumerable source);

Here is an example of calling this method:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

public class Exercise : Form
{
    private ListBox lbxNumbers;
    private Label lblAverage;
    private TextBox txtAverage;

    public Exercise()
    {
        InitializeComponent();
    }

    void InitializeComponent()
    {
        lbxNumbers = new ListBox();
        lbxNumbers.Size = new Size(105, 80);
        lbxNumbers.Location = new System.Drawing.Point(12, 12);

        lblAverage = new Label();
        lblAverage.AutoSize = true;
        lblAverage.Text = "Average:";
        lblAverage.Location = new Point(12, 90);

        txtAverage = new TextBox();
        txtAverage.Width = 50;
        txtAverage.Location = new Point(68, 90);
        txtAverage.TextAlign = HorizontalAlignment.Right;

        Text = "Numbers";
        MinimizeBox = false;
        MaximizeBox = false;
        Controls.Add(lbxNumbers);
        Controls.Add(lblAverage);
        Controls.Add(txtAverage);

        Load += new EventHandler(ExerciseLoad);
        Size = new System.Drawing.Size(140, 150);
        StartPosition = FormStartPosition.CenterScreen;
    }

    private void ExerciseLoad(object sender, EventArgs e)
    {
        var numbers = new double [] { 12.44, 525.38, 6.28, 2448.32, 632.04 };
        
        var number = from n
                     in numbers
                     select n;

        foreach (var member in number)
            lbxNumbers.Items.Add(member.ToString());

        txtAverage.Text = number.Average().ToString();
    }

    [STAThread]
    public static int Main()
    {
        System.Windows.Forms.Application.Run(new Exercise());
        return 0;
    }
}

This would produce:

Average

If it is only the average that you want, you can include the LINQ statement in parentheses and call the Average method outside. Here is an example:

var average = (from n
               in numbers
               select n).Average();

txtAverage.Text = average.ToString();

Of course, you can add a where condition if you want to restrict the result.

 

Previous Copyright © 2010-2011 C#Key Next