1. Computing

Using LINQ to extract sequences from other sequences

By

This is the sixth LINQ tutorial looking at aggregate methods and continues from five. The others can be accessed from these links below. If you don't know what Linq is, please start with tutorial one.

Take a look at the full list from Microsoft of all the Enumerable methods.

Aggregation Operators

We're still working our way through the Enumerable methods and we'll start with Aggregation Operators. Again if you know SQL, you may be familiar with count, max, min, sum and average. Linq has them as well, along with LongCount and Aggregate.

Let's look at Count first. This returns the number of elements in a sequence.

var list = new List {10, 2, 4, 5, 8, 12, 14};
Console.WriteLine("Number of Elements > 5:{0}",list.Where(n => n > 5).Count());

There' a more concise form.

Console.WriteLine("Number of Elements > 5:{0}",list.Count(n => n > 5));

LongCount is used the same as Count but returns an Int64.

Min and Max

These have overloads for all the numeric types so you can use them for ints,int64, doubles etc.

var list = new List {10, 2, 4, 5, 8, 12, 14};
var list2 = new List {10.4, 2.1, 4.1, -5.333, 8.0, 11.9999, -14.2};
Console.WriteLine("Lowest Value {0}",list.Min());
Console.WriteLine("Highest Value {0}",list2.Max());

This outputs:

Lowest Value 2
Highest Value 11.9999

It's possible to apply a tranformative method so instead of Min or Max on each element in the sequence, you are calling it on a function(each element).

class Program
{
    class Point
    {
      public int X { get; set; }
      public int Y { get; set; }
    }

    static void Main(string[] args)
    {
        var points = new List<Point>
            {
           new Point() {X = 10, Y = 5},
           new Point() {X = 12, Y = 7},
           new Point() {X = 19, Y = 12},
           };
        var list = new List<int> {10, 2, 4, 5, 8, 12, 14};
        Console.WriteLine("Lowest Value {0}",list.Max(n => n*n)) ;
        var dist = points.Min(d => Math.Sqrt( d.X*d.X + d.Y*d.Y )) ;
        Console.WriteLine("Shortest Distance {0}",dist) ;
        Console.ReadKey() ;
    }
}

In this case we have a simple Min which works out the maximum value of the squares of each element in list and the shortest distance from (0,0) of the three points in the points list. Both

The output is

Lowest Value 196
Shortest Distance 11.1803398874989

Average Method

Just like Min and Max, this processes the list and returns the average value. Using the points list from above, this outputs:

Console.Write("Average of the X coordinates is {0}",points.Average(x => x.X));

returns a value of 13.6666666666667.

The Aggregate Method

Just as with Min and Max, the Aggregate method applies a function to each element in a list and returns a value. Specifically you can apply a function to filter the list.

In the example below, we have a list of Points and I want to know which point is nearer another point. Amazingly and somewhat confusingly, this can be done in just one Aggregate statement. It uses the Point class and points array from the earlier examples.

var aPoint = new Point() {X = 13, Y = 11};
var closest = points.Aggregate(aPoint,
     (nearest, next) =>
     next.X*next.X + next.Y*next.Y <
     nearest.X*nearest.X + nearest.Y*nearest.Y
     ? next
     : nearest);
Console.WriteLine("Closest to aPoint is {0},{1}", closest.X, closest.Y);

Admittedly we are getting very close to "What was the programmer smoking?" here and there may well be a faster algorithm but it shows what you can do.

There's a further overload which adds a result selector, but rather than make this example even more complicated, instead I refer you to the official documentation example.

Projection

This is about the methods Select and SelectMany. Thinking back to SQL, when you do a select, you can do a Select * or a select column 1, column 2 instead where you output a subset of available columns. With Select we can take each element in the sequence and transform it. This is called Projection. This can include the original elements or some aspect such as the length of a string.

var words = new List(){"Aby","Bart","Charles","David"};
var lengths = words.Select(l => l.Length);
foreach(var l in lengths)
  Console.WriteLine("{0} ",l);

This takes the list of words and generates a list of those words lengths.

Another use is to apply Select to a sequence and generate a new Anonymous type from it. The second form of Select includes the index of each element so using the same list oif words above.

var lengths = words.Select((l, index) =>
      new
      {
          index,
          l.Length
      }
    ) ;

foreach(var l in lengths)
  Console.WriteLine("{0} ",l) ;

This outputs:

{ index = 0, Length = 3 }
{ index = 1, Length = 4 }
{ index = 2, Length = 7 }
{ index = 3, Length = 5 }

SelectMany

This flattens a sequence. In the example below

        class Division
        {
            public string name { get; set; }
            public List<string> Regiments { get; set; }
        }

        static void Main(string[] args)
        {
            Division[] army = {
               new Division {name="1st",Regiments = new List<string>{ "4th","5th","6th"}},
                new Division {name="2nd",Regiments = new List<string>{ "1st","2nd","3rd"}},
                new Division {name="3rd",Regiments = new List<string>{ "7th","8th"}}};
            var ArmyRegiments = army.SelectMany(Division
=> Division.Regiments) ;
            foreach(var regt in ArmyRegiments)
              Console.WriteLine("{0} ",regt) ;
            Console.ReadKey() ;
        }

It outputs this single list. 4th
5th
6th
1st
2nd
3rd
7th
8th

Although I used an array for army, a List would work equally well. Just change the line

            Division[] army = {

to

            List<Division> army = new List<Division> {

In the next and hopefully last Linq tutorial, I'll cover grouping and joing and converting to Arrays, Dictionaries, Lookups etc.

©2014 About.com. All rights reserved.