8 Ways to Partition Elements in a LINQ Query Using the Take and Skip Methods

LINQ – The Take and Skip methods

The partitioning LINQ methods are used to fetch or skip elements from the beginning of an enumerable list while a condition yields true. The Take and Skip methods, and TakeWhile and SkipWhile methods, are complements of one another, meaning that if we concatenate the results from any of these two functional complements we will end up with the same sequence that we started with. These methods are available from.NET Framework 3.5 and onwards.

We will be using IEnumerable<T> collections to hold the films and film studios that are used in the example code. The film collection is of the type List<Film> and is named films and the film studio collection is of the type List<FilmStudio> and is named filmStudios.

The return value from the methods is an IEnumerable<T> containing the items satisfying the condition that you can continue to query; and all necessary information needed to perform the action is stored inside the result.

You can use the methods as extension methods (instance methods) on any object that is of type IEnumerable<T>.

Deferred execution is used when calling any of the methods, which means that the query will not be executed until either a foreach loop is used, or one of the GetEnumerator, ToList, ToArray or ToDictionary methods is called directly on the on the result.

If the source collection is null, an ArgumentNullException exception will be thrown.

Note that we don’t have to specify a type when calling the methods, the pre-compiler will figure out the correct type automatically.

These are the variations of the four methods that we will be looking at in this article.

  • Take<T>
  • The Take<T> Method with a Nested Query
  • TakeWhile<T>
  • TakeWhile<T> Indexed
  • Skip<T>
  • Skip<T> Nested
  • SkipWhile<T>
  • SkipWhile<T> Indexed

The Take<T> Method

The Take method is used to fetch items from the beginning of an enumerable sequence for as long as the limiting condition is true.

If the number of items stated in the Take methods’ parenthesis is greater than the available number of items in the collection, all items will be returned. If the number of items stated is less than one an empty result is returned.

Let’s say we have a collection of Film objects from which we want fetch the first 3 films, after the Take method has been called the result would be of the System.Linq.Enumerable.TakeIterator<Film> type.

var result = films.Take(3);

If you prefer to use LINQ syntax, the following code would yield the same result.

var result = (

from f in films

select f

).Take(3);

Using the sample data the query would return:

———- TAKE ———-

Star Wars

The Hobbit

Mad Max

The Take<T> Method with a Nested Query

The Take method can be used on a nested LINQ query to fetch a pre-determined number of items. The following example fetches the first 3 films and their corresponding film studio; to achieve this we use a second LINQ query nested within the first query. We then use the Take method on the outer LINQ query and store the each of the films using instances of the ExtendedFilm class.

var result = (

from f in films

select new ExtendedFilm

{

Title = f.Title,

Studio = (

from s in filmStudios

where s.ID == f.StudioID

select s.Name).First(),

Rank = f.Rank

}

).Take(3);

Using the sample data the query would return:

———- TAKE NESTED ———-

Star Wars Lucas Arts

The Hobbit MGM

Mad Max MGM

The TakeWhile<T> Method

The TakeWhile method is very similar to the Take method with the difference that it uses a limiting Lambda expression as a means to determine which items that will be fetched; items will be fetched as long as the expression yields true.

Let’s say we have a collection of Film objects from which we want fetch all the films up to the film with the title “Mad Max”, after the TakeWhile method has been called the result would be of the System.Linq.Enumerable.TakeWhileIterator<Film> type.

var result = films.TakeWhile(f => f.Title!= “Mad Max”);

The following use LINQ syntax would yield the same result.

var result = (

from f in films

select f

).TakeWhile(f => f.Title!= “Mad Max”);

Using the sample data the query would return:

———- TAKE WHILE ———-

Star Wars

The Hobbit

The TakeWhile<T> Method Indexed

The indexed TakeWhile differs from the Take method in that it takes limiting Lambda (predicate) function as a parameter; the function has two parameters, the first is the current item and the second is the index of the current item. The TakeWhile method will return items as long as the condition of the predicate function yields true

The following sample code would return any film from the source sequence starting from the beginning for as long as the index is less than the StudioID.

var result = films.TakeWhile((f, index) => index < f.StudioID);

Using the sample data the query would return:

———- TAKE WHILE INDEXED ———-

Star Wars

The Hobbit

The Skip<T> Method

The Skip method will skip a given number of items from the beginning of an enumerable sequence and return the remaining items from the sequence.

If we pass in a value greater than the available number of items in the collection in the Skip methods’ parenthesis, an empty result will be returned; if the passed in value is less than one all items will be returned.

Let’s say we have a collection of Film objects from which we want skip the first 3 films, the result would be of the System.Linq.Enumerable.SkipIterator< Film> type after the Skip method has been called.

var result = films.Skip(3);

The same result would be fetched using the following LINQ syntax.

var result = (

from f in films

select f

).Skip(3);

Using the sample data the query would return:

———- SKIP ———-

Monsters Inc.

Toy Story

RoboCop

Captain Phillips

The Skip<T> Method with a Nested Query

You can still use the Skip method even if it is on a nested LINQ query if you want to skip a number of items in the sequence and fetch the remaining items.

The following example we fetches the remaining films and their corresponding film studio after we have skipped the 3 first items in the collection. To fetch the film studio name we use a second LINQ query that we assign to the Studio property of the ExtendedFilm class instances that are created for each fetched item.

var result = (

from f in films

select new ExtendedFilm

{

Title = f.Title,

Studio = (

from s in filmStudios

where s.ID == f.StudioID

select s.Name).First(),

Rank = f.Rank

}

).Skip(3);

Using the sample data the query would return:

———- SKIP NESTED ———-

Monsters Inc. Pixar

Toy Story Pixar

RoboCop Sony Pictures

Captain Phillips Sony Pictures

The SkipWhile<T> Method

As long as the limiting Lambda expression yields true that is passed in as a parameter to the SkipWhile method, items will be skipped starting from the beginning of the enumerable sequence; once the expression yields false the remaining items in the sequence are returned.

Let’s say we have a collection of Film objects from which we want fetch all the films starting with “Mad Max”, the result would be of the System.Linq.Enumerable.SkipWhileIterator< Film> type after the SkipWhile method has been called.

var result = films.SkipWhile(f => f.Title!= “Mad Max”);

The following code would yield the same result.

var result = (

from f in films

select f

).SkipWhile(f => f.Title!= “Mad Max”);

Using the sample data the query would return:

———- SKIP WHILE ———-

Mad Max

Monsters Inc.

Toy Story

RoboCop

Captain Phillips

The SkipWhile<T> Method Indexed

The indexed SkipWhile method is very similar to the Skip method, with the difference that it uses a limiting Lambda (predicate) function to determine which items that should be skipped; items will be skipped starting from the beginning of the sequence as long as the expression yields true; a zero-based index that is passed into the function that represent the position of the current item in the source sequence.

The following sample code would skip any film from the source sequence starting from the beginning of the sequence for as long as the index is less than the StudioID.

var result = films.SkipWhile((f, index) => index < f.StudioID);

Using the sample data the query would return:

———- SKIP WHILE INDEXED ———-

Mad Max

Monsters Inc.

Toy Story

RoboCop

Captain Phillips

Example data

public List<Film> films = new List<Film>()

{

new Film{Title=”Star Wars”, StudioID=001,

Rank = new List<double> {7.8, 5.6, 9.5}},

new Film{Title=”The Hobbit”, StudioID=002, ActorID = 002,

Rank = new List<double> {7.2, 6.6, 8.5}},

new Film{Title=”Mad Max”, StudioID=002, ActorID = 001,

Rank = new List<double> {6.8, 5.6, 9.0}},

new Film{Title=”Monsters Inc.”, StudioID=003,

Rank = new List<double> {7.8, 7.6, 9.2}},

new Film{Title=”Toy Story”, StudioID=003,

Rank = new List<double> {6.8, 6.6, 6.5}},

new Film{Title=”RoboCop”, StudioID=005,

Rank = new List<double> {7.6, 7.7, 7.5}},

new Film{Title=”Captain Phillips”, StudioID=005, ActorID = 003,

Rank = new List<double> {8.4, 8.5, 8.6}}

};

public List<FilmStudio> filmStudios = new List<FilmStudio>()

{

new FilmStudio(){Name=”Lucas Arts”, ID=001},

new FilmStudio(){Name=”MGM”, ID=002},

new FilmStudio(){Name=”Pixar”, ID=003},

new FilmStudio(){Name=”HBO Films”, ID=004},

new FilmStudio(){Name=”Sony Pictures”, ID=005}

};

Example Classes

class Film

{

public string Title { get; set; }

public int StudioID { get; set; }

public int ActorID { get; set; }

public List<double> Rank { get; set; }

}

class ExtendedFilm: Film

{

public string Studio { get; set; }

}

class FilmStudio

{

public string Name { get; set; }

public int ID { get; set; }

}

Source by Jonas Fagerberg

Leave a Comment