C# LanguageLINQ Queries

Introduction

LINQ is an acronym which stands for Language INtegrated Query. It is a concept which integrates a query language by offering a consistent model for working with data across various kinds of data sources and formats; you use the same basic coding patterns to query and transform data in XML documents, SQL databases, ADO.NET Datasets, .NET collections, and any other format for which a LINQ provider is available.

Syntax

  • Query syntax :

    • from <range variable> in <collection>
    • [from <range variable> in <collection>, ...]
    • <filter, joining, grouping, aggregate operators, ...> <lambda expression>
    • <select or groupBy operator> <formulate the result>
  • Method syntax :

    • Enumerable.Aggregate(func)
    • Enumerable.Aggregate(seed, func)
    • Enumerable.Aggregate(seed, func, resultSelector)
    • Enumerable.All(predicate)
    • Enumerable.Any()
    • Enumerable.Any(predicate)
    • Enumerable.AsEnumerable()
    • Enumerable.Average()
    • Enumerable.Average(selector)
    • Enumerable.Cast<Result>()
    • Enumerable.Concat(second)
    • Enumerable.Contains(value)
    • Enumerable.Contains(value, comparer)
    • Enumerable.Count()
    • Enumerable.Count(predicate)
    • Enumerable.DefaultIfEmpty()
    • Enumerable.DefaultIfEmpty(defaultValue)
    • Enumerable.Distinct()
    • Enumerable.Distinct(comparer)
    • Enumerable.ElementAt(index)
    • Enumerable.ElementAtOrDefault(index)
    • Enumerable.Empty()
    • Enumerable.Except(second)
    • Enumerable.Except(second, comparer)
    • Enumerable.First()
    • Enumerable.First(predicate)
    • Enumerable.FirstOrDefault()
    • Enumerable.FirstOrDefault(predicate)
    • Enumerable.GroupBy(keySelector)
    • Enumerable.GroupBy(keySelector, resultSelector)
    • Enumerable.GroupBy(keySelector, elementSelector)
    • Enumerable.GroupBy(keySelector, comparer)
    • Enumerable.GroupBy(keySelector, resultSelector, comparer)
    • Enumerable.GroupBy(keySelector, elementSelector, resultSelector)
    • Enumerable.GroupBy(keySelector, elementSelector, comparer)
    • Enumerable.GroupBy(keySelector, elementSelector, resultSelector, comparer)
    • Enumerable.Intersect(second)
    • Enumerable.Intersect(second, comparer)
    • Enumerable.Join(inner, outerKeySelector, innerKeySelector, resultSelector)
    • Enumerable.Join(inner, outerKeySelector, innerKeySelector, resultSelector, comparer)
    • Enumerable.Last()
    • Enumerable.Last(predicate)
    • Enumerable.LastOrDefault()
    • Enumerable.LastOrDefault(predicate)
    • Enumerable.LongCount()
    • Enumerable.LongCount(predicate)
    • Enumerable.Max()
    • Enumerable.Max(selector)
    • Enumerable.Min()
    • Enumerable.Min(selector)
    • Enumerable.OfType<TResult>()
    • Enumerable.OrderBy(keySelector)
    • Enumerable.OrderBy(keySelector, comparer)
    • Enumerable.OrderByDescending(keySelector)
    • Enumerable.OrderByDescending(keySelector, comparer)
    • Enumerable.Range(start, count)
    • Enumerable.Repeat(element, count)
    • Enumerable.Reverse()
    • Enumerable.Select(selector)
    • Enumerable.SelectMany(selector)
    • Enumerable.SelectMany(collectionSelector, resultSelector)
    • Enumerable.SequenceEqual(second)
    • Enumerable.SequenceEqual(second, comparer)
    • Enumerable.Single()
    • Enumerable.Single(predicate)
    • Enumerable.SingleOrDefault()
    • Enumerable.SingleOrDefault(predicate)
    • Enumerable.Skip(count)
    • Enumerable.SkipWhile(predicate)
    • Enumerable.Sum()
    • Enumerable.Sum(selector)
    • Enumerable.Take(count)
    • Enumerable.TakeWhile(predicate)
    • orderedEnumerable.ThenBy(keySelector)
    • orderedEnumerable.ThenBy(keySelector, comparer)
    • orderedEnumerable.ThenByDescending(keySelector)
    • orderedEnumerable.ThenByDescending(keySelector, comparer)
    • Enumerable.ToArray()
    • Enumerable.ToDictionary(keySelector)
    • Enumerable.ToDictionary(keySelector, elementSelector)
    • Enumerable.ToDictionary(keySelector, comparer)
    • Enumerable.ToDictionary(keySelector, elementSelector, comparer)
    • Enumerable.ToList()
    • Enumerable.ToLookup(keySelector)
    • Enumerable.ToLookup(keySelector, elementSelector)
    • Enumerable.ToLookup(keySelector, comparer)
    • Enumerable.ToLookup(keySelector, elementSelector, comparer)
    • Enumerable.Union(second)
    • Enumerable.Union(second, comparer)
    • Enumerable.Where(predicate)
    • Enumerable.Zip(second, resultSelector)

Remarks

To use LINQ queries you need to import System.Linq.

The Method Syntax is more powerful and flexible, but the Query Syntax may be simpler and more familiar. All queries written in Query syntax are translated into the functional syntax by the compiler, so performance is the same.

Query objects are not evaluated until they are used, so they can be changed or added to without a performance penalty.

Where

Returns a subset of items which the specified predicate is true for them.

List<string> trees = new List<string>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };

Method syntax

// Select all trees with name of length 3
var shortTrees = trees.Where(tree => tree.Length == 3); // Oak, Elm

Query syntax

var shortTrees = from tree in trees
                 where tree.Length == 3
                 select tree; // Oak, Elm

Select - Transforming elements

Select allows you to apply a transformation to every element in any data structure implementing IEnumerable.

Getting the first character of each string in the following list:

List<String> trees = new List<String>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };

Using regular (lambda) syntax

//The below select stament transforms each element in tree into its first character.
IEnumerable<String> initials = trees.Select(tree => tree.Substring(0, 1));
foreach (String initial in initials) {
    System.Console.WriteLine(initial);
}

Output:

O
B
B
E
H
M

Live Demo on .NET Fiddle

Using LINQ Query Syntax

initials = from tree in trees
           select tree.Substring(0, 1);

Chaining methods

Many LINQ functions both operate on an IEnumerable<TSource> and also return an IEnumerable<TResult>. The type parameters TSource and TResult may or may not refer to the same type, depending on the method in question and any functions passed to it.

A few examples of this are

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

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, int, bool> predicate
)

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

While some method chaining may require an entire set to be worked prior to moving on, LINQ takes advantage of deferred execution by using yield return MSDN which creates an Enumerable and an Enumerator behind the scenes. The process of chaining in LINQ is essentially building an enumerable (iterator) for the original set -- which is deferred -- until materialized by enumerating the enumerable.

This allows these functions to be fluently chained wiki, where one function can act directly on the result of another. This style of code can be used to perform many sequence based operations in a single statement.

For example, it's possible to combine Select, Where and OrderBy to transform, filter and sort a sequence in a single statement.

var someNumbers = { 4, 3, 2, 1 };

var processed = someNumbers
        .Select(n => n * 2)   // Multiply each number by 2
        .Where(n => n != 6)   // Keep all the results, except for 6
        .OrderBy(n => n);     // Sort in ascending order

Output:

2
4
8

Live Demo on .NET Fiddle

Any functions that both extend and return the generic IEnumerable<T> type can be used as chained clauses in a single statement. This style of fluent programming is powerful, and should be considered when creating your own extension methods.

Range and Repeat

The Range and Repeat static methods on Enumerable can be used to generate simple sequences.

Range

Enumerable.Range() generates a sequence of integers given a starting value and a count.

// Generate a collection containing the numbers 1-100 ([1, 2, 3, ..., 98, 99, 100])
var range = Enumerable.Range(1,100);

Live Demo on .NET Fiddle

Repeat

Enumerable.Repeat() generates a sequence of repeating elements given an element and the number of repetitions required.

// Generate a collection containing "a", three times (["a","a","a"])
var repeatedValues = Enumerable.Repeat("a", 3);

Live Demo on .NET Fiddle

Skip and Take

The Skip method returns a collection excluding a number of items from the beginning of the source collection. The number of items excluded is the number given as an argument. If there are less items in the collection than specified in the argument then an empty collection is returned.

The Take method returns a collection containing a number of elements from the beginning of the source collection. The number of items included is the number given as an argument. If there are less items in the collection than specified in the argument then the collection returned will contain the same elements as the source collection.

var values = new [] { 5, 4, 3, 2, 1 };

var skipTwo        = values.Skip(2);         // { 3, 2, 1 }
var takeThree      = values.Take(3);         // { 5, 4, 3 }
var skipOneTakeTwo = values.Skip(1).Take(2); // { 4, 3 }
var takeZero       = values.Take(0);         // An IEnumerable<int> with 0 items

Live Demo on .NET Fiddle

Skip and Take are commonly used together to paginate results, for instance:

IEnumerable<T> GetPage<T>(IEnumerable<T> collection, int pageNumber, int resultsPerPage) {
    int startIndex = (pageNumber - 1) * resultsPerPage;
    return collection.Skip(startIndex).Take(resultsPerPage);
}

Warning: LINQ to Entities only supports Skip on ordered queries. If you try to use Skip without ordering you will get a NotSupportedException with the message "The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'."

First, FirstOrDefault, Last, LastOrDefault, Single, and SingleOrDefault

All six methods return a single value of the sequence type, and can be called with or without a predicate.

Depending on the number of elements that match the predicate or, if no predicate is supplied, the number of elements in the source sequence, they behave as follows:

First()

  • Returns the first element of a sequence, or the first element matching the provided predicate.
  • If the sequence contains no elements, an InvalidOperationException is thrown with the message: "Sequence contains no elements".
  • If the sequence contains no elements matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains no matching element".

Example

// Returns "a":
new[] { "a" }.First();

// Returns "a":
new[] { "a", "b" }.First();

// Returns "b":
new[] { "a", "b" }.First(x => x.Equals("b"));

// Returns "ba":
new[] { "ba", "be" }.First(x => x.Contains("b"));

// Throws InvalidOperationException:
new[] { "ca", "ce" }.First(x => x.Contains("b"));

// Throws InvalidOperationException:
new string[0].First();

Live Demo on .NET Fiddle

FirstOrDefault()

  • Returns the first element of a sequence, or the first element matching the provided predicate.
  • If the sequence contains no elements, or no elements matching the provided predicate, returns the default value of the sequence type using default(T).

Example

// Returns "a":
new[] { "a" }.FirstOrDefault();

// Returns "a":
new[] { "a", "b" }.FirstOrDefault();

// Returns "b":
new[] { "a", "b" }.FirstOrDefault(x => x.Equals("b"));

// Returns "ba":
new[] { "ba", "be" }.FirstOrDefault(x => x.Contains("b"));

// Returns null:
new[] { "ca", "ce" }.FirstOrDefault(x => x.Contains("b"));

// Returns null:
new string[0].FirstOrDefault();

Live Demo on .NET Fiddle

Last()

  • Returns the last element of a sequence, or the last element matching the provided predicate.
  • If the sequence contains no elements, an InvalidOperationException is thrown with the message "Sequence contains no elements."
  • If the sequence contains no elements matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains no matching element".

Example

// Returns "a":
new[] { "a" }.Last();

// Returns "b":
new[] { "a", "b" }.Last();

// Returns "a":
new[] { "a", "b" }.Last(x => x.Equals("a"));

// Returns "be":
new[] { "ba", "be" }.Last(x => x.Contains("b"));

// Throws InvalidOperationException:
new[] { "ca", "ce" }.Last(x => x.Contains("b"));

// Throws InvalidOperationException:
new string[0].Last(); 

LastOrDefault()

  • Returns the last element of a sequence, or the last element matching the provided predicate.
  • If the sequence contains no elements, or no elements matching the provided predicate, returns the default value of the sequence type using default(T).

Example

// Returns "a":
new[] { "a" }.LastOrDefault();

// Returns "b":
new[] { "a", "b" }.LastOrDefault();

// Returns "a":
new[] { "a", "b" }.LastOrDefault(x => x.Equals("a"));

 // Returns "be":
new[] { "ba", "be" }.LastOrDefault(x => x.Contains("b"));

// Returns null:
new[] { "ca", "ce" }.LastOrDefault(x => x.Contains("b")); 

// Returns null:
new string[0].LastOrDefault();

Single()

  • If the sequence contains exactly one element, or exactly one element matching the provided predicate, that element is returned.
  • If the sequence contains no elements, or no elements matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains no elements".
  • If the sequence contains more than one element, or more than one element matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains more than one element".
  • Note: in order to evaluate whether the sequence contains exactly one element, at most two elements has to be enumerated.

Example

// Returns "a":
new[] { "a" }.Single();

// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "b" }.Single();

// Returns "b":
new[] { "a", "b" }.Single(x => x.Equals("b"));

// Throws InvalidOperationException:
new[] { "a", "b" }.Single(x => x.Equals("c"));

// Throws InvalidOperationException:
new string[0].Single(); 

// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "a" }.Single();

SingleOrDefault()

  • If the sequence contains exactly one element, or exactly one element matching the provided predicate, that element is returned.
  • If the sequence contains no elements, or no elements matching the provided predicate, default(T) is returned.
  • If the sequence contains more than one element, or more than one element matching the provided predicate, an InvalidOperationException is thrown with the message "Sequence contains more than one element".
  • If the sequence contains no elements matching the provided predicate, returns the default value of the sequence type using default(T).
  • Note: in order to evaluate whether the sequence contains exactly one element, at most two elements has to be enumerated.

Example

// Returns "a":
new[] { "a" }.SingleOrDefault();

// returns "a"
new[] { "a", "b" }.SingleOrDefault(x => x == "a"); 

// Returns null:
new[] { "a", "b" }.SingleOrDefault(x => x == "c");

// Throws InvalidOperationException:
new[] { "a", "a" }.SingleOrDefault(x => x == "a");

// Throws InvalidOperationException:
new[] { "a", "b" }.SingleOrDefault();

// Returns null:
new string[0].SingleOrDefault();

Recommendations

  • Although you can use FirstOrDefault, LastOrDefault or SingleOrDefault to check whether a sequence contains any items, Any or Count are more reliable. This is because a return value of default(T) from one of these three methods doesn't prove that the sequence is empty, as the value of the first / last / single element of the sequence could equally be default(T)

  • Decide on which methods fits your code's purpose the most. For instance, use Single only if you must ensure that there is a single item in the collection matching your predicate — otherwise use First; as Single throw an exception if the sequence has more than one matching element. This of course applies to the "*OrDefault"-counterparts as well.

  • Regarding efficiency: Although it's often appropriate to ensure that there is only one item (Single) or, either only one or zero (SingleOrDefault) items, returned by a query, both of these methods require more, and often the entirety, of the collection to be examined to ensure there in no second match to the query. This is unlike the behavior of, for example, the First method, which can be satisfied after finding the first match.

Except

The Except method returns the set of items which are contained in the first collection but are not contained in the second. The default IEqualityComparer is used to compare the items within the two sets. There is an overload which accepts an IEqualityComparer as an argument.

Example:

int[] first = { 1, 2, 3, 4 };
int[] second = { 0, 2, 3, 5 };

IEnumerable<int> inFirstButNotInSecond = first.Except(second);
// inFirstButNotInSecond = { 1, 4 }

Output:

1
4

Live Demo on .NET Fiddle

In this case .Except(second) excludes elements contained in the array second, namely 2 and 3 (0 and 5 are not contained in the first array and are skipped).

Note that Except implies Distinct (i.e., it removes repeated elements). For example:

int[] third = { 1, 1, 1, 2, 3, 4 };

IEnumerable<int> inThirdButNotInSecond = third.Except(second);
// inThirdButNotInSecond = { 1, 4 }

Output:

1
4

Live Demo on .NET Fiddle

In this case, the elements 1 and 4 are returned only once.


Implementing IEquatable or providing the function an IEqualityComparer will allow using a different method to compare the elements. Note that the GetHashCode method should also be overridden so that it will return an identical hash code for object that are identical according to the IEquatable implementation.

Example With IEquatable:

class Holiday : IEquatable<Holiday>
{
    public string Name { get; set; }

    public bool Equals(Holiday other)
    {
        return Name == other.Name;
    }

    // GetHashCode must return true whenever Equals returns true.
    public override int GetHashCode()
    {
        //Get hash code for the Name field if it is not null.
        return Name?.GetHashCode() ?? 0;
    }
}

public class Program
{
    public static void Main()
    {
        List<Holiday> holidayDifference = new List<Holiday>();

        List<Holiday> remoteHolidays = new List<Holiday>
        {
            new Holiday { Name = "Xmas" },
            new Holiday { Name = "Hanukkah" },
            new Holiday { Name = "Ramadan" }
        };

        List<Holiday> localHolidays = new List<Holiday>
        {
            new Holiday { Name = "Xmas" },
            new Holiday { Name = "Ramadan" }
        };

        holidayDifference = remoteHolidays
            .Except(localHolidays)
            .ToList();

        holidayDifference.ForEach(x => Console.WriteLine(x.Name));
    }
}

Output:

Hanukkah

Live Demo on .NET Fiddle

SelectMany: Flattening a sequence of sequences

var sequenceOfSequences = new [] { new [] { 1, 2, 3 }, new [] { 4, 5 }, new [] { 6 } };
var sequence = sequenceOfSequences.SelectMany(x => x);
// returns { 1, 2, 3, 4, 5, 6 }

Use SelectMany() if you have, or you are creating a sequence of sequences, but you want the result as one long sequence.

In LINQ Query Syntax:

var sequence = from subSequence in sequenceOfSequences
               from item in subSequence
               select item;

If you have a collection of collections and would like to be able to work on data from parent and child collection at the same time, it is also possible with SelectMany.

Let's define simple classes

public class BlogPost
{
    public int Id { get; set; }
    public string Content { get; set; }
    public List<Comment> Comments { get; set; }
}

public class Comment
{
    public int Id { get; set; }
    public string Content { get; set; }
}

Let's assume we have following collection.

List<BlogPost> posts = new List<BlogPost>()
{
    new BlogPost()
    {
        Id = 1,
        Comments = new List<Comment>()
        {
            new Comment()
            {
                Id = 1,
                Content = "It's really great!",
            },
            new Comment()
            {
                Id = 2,
                Content = "Cool post!"
            }
        }
    },
    new BlogPost()
    {
        Id = 2,
        Comments = new List<Comment>()
        {
            new Comment()
            {
                Id = 3,
                Content = "I don't think you're right",
            },
            new Comment()
            {
                Id = 4,
                Content = "This post is a complete nonsense"
            }
        }
    }
};

Now we want to select comments Content along with Id of BlogPost associated with this comment. In order to do so, we can use appropriate SelectMany overload.

var commentsWithIds = posts.SelectMany(p => p.Comments, (post, comment) => new { PostId = post.Id, CommentContent = comment.Content });

Our commentsWithIds looks like this

{
    PostId = 1,
    CommentContent = "It's really great!"
},
{
    PostId = 1,
    CommentContent = "Cool post!"
},
{
    PostId = 2,
    CommentContent = "I don't think you're right"
},
{
    PostId = 2,
    CommentContent = "This post is a complete nonsense"
}

SelectMany

The SelectMany linq method 'flattens' an IEnumerable<IEnumerable<T>> into an IEnumerable<T>. All of the T elements within the IEnumerable instances contained in the source IEnumerable will be combined into a single IEnumerable.

var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }

If you use a selector function which turns input elements into sequences, the result will be the elements of those sequences returned one by one.

Note that, unlike Select(), the number of elements in the output doesn't need to be the same as were in the input.

More real-world example

class School
{
    public Student[] Students { get; set; }
}

class Student 
{
    public string Name { get; set; }
}    
  
var schools = new [] {
    new School(){ Students = new [] { new Student { Name="Bob"}, new Student { Name="Jack"} }},
    new School(){ Students = new [] { new Student { Name="Jim"}, new Student { Name="John"} }}
};
               
var allStudents = schools.SelectMany(s=> s.Students);
             
foreach(var student in allStudents)
{
    Console.WriteLine(student.Name);
}

Output:

Bob
Jack
Jim
John

Live Demo on .NET Fiddle

All

All is used to check, if all elements of a collection match a condition or not.
see also: .Any

1. Empty parameter

All: is not allowed to be used with empty parameter.

2. Lambda expression as parameter

All: Returns true if all elements of collection satisfies the lambda expression and false otherwise:

var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.All(i => i < 10); // true
bool result = numbers.All(i => i >= 3); // false

3. Empty collection

All: Returns true if the collection is empty and a lambda expression is supplied:

var numbers = new List<int>();
bool result = numbers.All(i => i >= 0); // true

Note: All will stop iteration of the collection as soon as it finds an element not matching the condition. This means that the collection will not necessarily be fully enumerated; it will only be enumerated far enough to find the first item not matching the condition.

Query collection by type / cast elements to type

interface IFoo { }
class Foo : IFoo { }
class Bar : IFoo { }

var item0 = new Foo();
var item1 = new Foo();
var item2 = new Bar();
var item3 = new Bar();
var collection = new IFoo[] { item0, item1, item2, item3 };

Using OfType

var foos = collection.OfType<Foo>(); // result: IEnumerable<Foo> with item0 and item1
var bars = collection.OfType<Bar>(); // result: IEnumerable<Bar> item item2 and item3
var foosAndBars = collection.OfType<IFoo>(); // result: IEnumerable<IFoo> with all four items

Using Where

var foos = collection.Where(item => item is Foo); // result: IEnumerable<IFoo> with item0 and item1
var bars = collection.Where(item => item is Bar); // result: IEnumerable<IFoo> with item2 and item3

Using Cast

var bars = collection.Cast<Bar>();                // throws InvalidCastException on the 1st item
var foos = collection.Cast<Foo>();                // throws InvalidCastException on the 3rd item
var foosAndBars = collection.Cast<IFoo>();        // OK 

Union

Merges two collections to create a distinct collection using the default equality comparer

int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 2, 3, 4, 5 };

var allElement = numbers1.Union(numbers2);   // AllElement now contains 1,2,3,4,5

Live Demo on .NET Fiddle

JOINS

Joins are used to combine different lists or tables holding data via a common key.

Like in SQL, the following kinds of Joins are supported in LINQ:
Inner, Left, Right, Cross and Full Outer Joins.

The following two lists are used in the examples below:

var first = new List<string>(){ "a","b","c"}; // Left data
var second = new List<string>(){ "a", "c", "d"}; // Right data

(Inner) Join

var result = from f in first
             join s in second on f equals s
             select new { f, s };

var result = first.Join(second, 
                        f => f, 
                        s => s,
                        (f, s) => new { f, s });

// Result: {"a","a"}
//         {"c","c"}

Left outer join

var leftOuterJoin = from f in first
                    join s in second on f equals s into temp
                    from t in temp.DefaultIfEmpty()
                    select new { First = f, Second = t};

// Or can also do:
var leftOuterJoin = from f in first
                    from s in second.Where(x => x == f).DefaultIfEmpty()
                    select new { First = f, Second = s};

// Result: {"a","a"}
//         {"b", null}  
//         {"c","c"}  


// Left outer join method syntax
var leftOuterJoinFluentSyntax = first.GroupJoin(second,
                                      f => f,
                                      s => s,
                                      (f, s) => new { First = f, Second = s })
                                   .SelectMany(temp => temp.Second.DefaultIfEmpty(),
                                      (f, s) => new { First = f.First, Second = s });

Right Outer Join

var rightOuterJoin = from s in second
                     join f in first on s equals f into temp
                     from t in temp.DefaultIfEmpty()
                     select new {First=t,Second=s};

// Result: {"a","a"}
//         {"c","c"}  
//         {null,"d"}  

Cross Join

var CrossJoin = from f in first
                from s in second
                select new { f, s };

// Result: {"a","a"}
//         {"a","c"}  
//         {"a","d"}  
//         {"b","a"}
//         {"b","c"}  
//         {"b","d"}  
//         {"c","a"}
//         {"c","c"}  
//         {"c","d"}

Full Outer Join

var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);

// Result: {"a","a"}
//         {"b", null}  
//         {"c","c"}  
//         {null,"d"}

Practical example

The examples above have a simple data structure so you can focus on understanding the different LINQ joins technically, but in the real world you would have tables with columns you need to join.

In the following example, there is just one class Region used, in reality you would join two or more different tables which hold the same key (in this example first and second are joined via the common key ID).

Example: Consider the following data structure:

public class Region 
{
    public Int32 ID;
    public string RegionDescription;
    
    public Region(Int32 pRegionID, string pRegionDescription=null)
    {
        ID = pRegionID; RegionDescription = pRegionDescription;
    }
}

Now prepare the data (i.e. populate with data):

// Left data
var first = new List<Region>() 
                 { new Region(1), new Region(3), new Region(4) }; 
// Right data
var second = new List<Region>() 
                 { 
                    new Region(1, "Eastern"),  new Region(2, "Western"),
                    new Region(3, "Northern"), new Region(4, "Southern")
                 }; 

You can see that in this example first doesn't contain any region descriptions so you want to join them from second. Then the inner join would look like:

// do the inner join
var result = from f in first
             join s in second on f.ID equals s.ID
             select new { f.ID, s.RegionDescription };


 // Result: {1,"Eastern"}
 //         {3, Northern}  
 //         {4,"Southern"}  

This result has created anonymous objects on the fly, which is fine, but we have already created a proper class - so we can specify it: Instead of select new { f.ID, s.RegionDescription }; we can say select new Region(f.ID, s.RegionDescription);, which will return the same data but will create objects of type Region - that will maintain compatibility with the other objects.

Live demo on .NET fiddle

Distinct

Returns unique values from an IEnumerable. Uniqueness is determined using the default equality comparer.

int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };

var distinct = array.Distinct();
// distinct = { 1, 2, 3, 4, 5 }

To compare a custom data type, we need to implement the IEquatable<T> interface and provide GetHashCode and Equals methods for the type. Or the equality comparer may be overridden:

class SSNEqualityComparer : IEqualityComparer<Person> {
    public bool Equals(Person a, Person b) => return a.SSN == b.SSN;
    public int GetHashCode(Person p) => p.SSN;
}

List<Person> people;

distinct = people.Distinct(SSNEqualityComparer);

GroupBy one or multiple fields

Lets assume we have some Film model:

public class Film {
    public string Title { get; set; }
    public string Category { get; set; }
    public int Year { get; set; }
}

Group by Category property:

foreach (var grp in films.GroupBy(f => f.Category)) {
    var groupCategory = grp.Key;
    var numberOfFilmsInCategory = grp.Count();
}

Group by Category and Year:

foreach (var grp in films.GroupBy(f => new { Category = f.Category, Year = f.Year })) {
    var groupCategory = grp.Key.Category;
    var groupYear = grp.Key.Year;
    var numberOfFilmsInCategory = grp.Count();
}

Using Range with various Linq methods

You can use the Enumerable class alongside Linq queries to convert for loops into Linq one liners.

Select Example

Opposed to doing this:

var asciiCharacters = new List<char>();
for (var x = 0; x < 256; x++)
{
    asciiCharacters.Add((char)x);
}

You can do this:

var asciiCharacters = Enumerable.Range(0, 256).Select(a => (char) a);

Where Example

In this example, 100 numbers will be generated and even ones will be extracted

var evenNumbers = Enumerable.Range(1, 100).Where(a => a % 2 == 0);

Query Ordering - OrderBy() ThenBy() OrderByDescending() ThenByDescending()

string[] names= { "mark", "steve", "adam" };

Ascending:

Query Syntax

var sortedNames =
    from name in names
    orderby name
    select name;

Method Syntax

var sortedNames = names.OrderBy(name => name);

sortedNames contains the names in following order: "adam","mark","steve"

Descending:

Query Syntax

var sortedNames =
    from name in names
    orderby name descending
    select name;

Method Syntax

var sortedNames = names.OrderByDescending(name => name);

sortedNames contains the names in following order: "steve","mark","adam"

Order by several fields

Person[] people =
{
    new Person { FirstName = "Steve", LastName = "Collins", Age = 30},
    new Person { FirstName = "Phil" , LastName = "Collins", Age = 28},
    new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 29},
    new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 15}
};

Query Syntax

var sortedPeople = from person in people
                   orderby person.LastName, person.FirstName, person.Age descending
                   select person;

Method Syntax

 sortedPeople = people.OrderBy(person => person.LastName)
                      .ThenBy(person => person.FirstName)
                      .ThenByDescending(person => person.Age);

Result

1. Adam Ackerman 29
2. Adam Ackerman 15
3. Phil Collins  28
4. Steve Collins 30

Basics

LINQ is largely beneficial for querying collections (or arrays).

For example, given the following sample data:

var classroom = new Classroom
{
    new Student { Name = "Alice", Grade = 97, HasSnack = true  },
    new Student { Name = "Bob",   Grade = 82, HasSnack = false },
    new Student { Name = "Jimmy", Grade = 71, HasSnack = true  },
    new Student { Name = "Greg",  Grade = 90, HasSnack = false },
    new Student { Name = "Joe",   Grade = 59, HasSnack = false }
}

We can "query" on this data using LINQ syntax. For example, to retrieve all students who have a snack today:

var studentsWithSnacks = from s in classroom.Students
                         where s.HasSnack
                         select s;

Or, to retrieve students with a grade of 90 or above, and only return their names, not the full Student object:

var topStudentNames = from s in classroom.Students
                      where s.Grade >= 90
                      select s.Name;

The LINQ feature is comprised of two syntaxes that perform the same functions, have nearly identical performance, but are written very differently. The syntax in the example above is called query syntax. The following example, however, illustrates method syntax. The same data will be returned as in the example above, but the way the query is written is different.

var topStudentNames = classroom.Students
                               .Where(s => s.Grade >= 90)
                               .Select(s => s.Name);

GroupBy

GroupBy is an easy way to sort a IEnumerable<T> collection of items into distinct groups.

Simple Example

In this first example, we end up with two groups, odd and even items.

List<int> iList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var grouped = iList.GroupBy(x => x % 2 == 0);

//Groups iList into odd [13579] and even[2468] items 
       
foreach(var group in grouped)
{
    foreach (int item in group)
    {
        Console.Write(item); // 135792468  (first odd then even)
    }
}

More Complex Example

Let's take grouping a list of people by age as an example. First, we'll create a Person object which has two properties, Name and Age.

public class Person
{
    public int Age {get; set;}
    public string Name {get; set;}
}

Then we create our sample list of people with various names and ages.

List<Person> people = new List<Person>();
people.Add(new Person{Age = 20, Name = "Mouse"});
people.Add(new Person{Age = 30, Name = "Neo"});
people.Add(new Person{Age = 40, Name = "Morpheus"});
people.Add(new Person{Age = 30, Name = "Trinity"});
people.Add(new Person{Age = 40, Name = "Dozer"});
people.Add(new Person{Age = 40, Name = "Smith"});

Then we create a LINQ query to group our list of people by age.

var query = people.GroupBy(x => x.Age);

Doing so, we can see the Age for each group, and have a list of each person in the group.

foreach(var result in query)
{
    Console.WriteLine(result.Key);
                
    foreach(var person in result)
        Console.WriteLine(person.Name);
}

This results in the following output:

20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith

You can play with the live demo on .NET Fiddle

Any

Any is used to check if any element of a collection matches a condition or not.
see also: .All, Any and FirstOrDefault: best practice

1. Empty parameter

Any: Returns true if the collection has any elements and false if the collection is empty:

var numbers = new List<int>();
bool result = numbers.Any(); // false

var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.Any(); //true

2. Lambda expression as parameter

Any: Returns true if the collection has one or more elements that meet the condition in the lambda expression:

var arrayOfStrings = new string[] { "a", "b", "c" };
arrayOfStrings.Any(item => item == "a");    // true
arrayOfStrings.Any(item => item == "d");    // false

3. Empty collection

Any: Returns false if the collection is empty and a lambda expression is supplied:

var numbers = new List<int>();
bool result = numbers.Any(i => i >= 0); // false

Note: Any will stop iteration of the collection as soon as it finds an element matching the condition. This means that the collection will not necessarily be fully enumerated; it will only be enumerated far enough to find the first item matching the condition.

Live Demo on .NET Fiddle

ToDictionary

The ToDictionary() LINQ method can be used to generate a Dictionary<TKey, TElement> collection based on a given IEnumerable<T> source.

IEnumerable<User> users = GetUsers();
Dictionary<int, User> usersById = users.ToDictionary(x => x.Id);

In this example, the single argument passed to ToDictionary is of type Func<TSource, TKey>, which returns the key for each element.

This is a concise way to perform the following operation:

Dictionary<int, User> usersById = new Dictionary<int User>();
foreach (User u in users) 
{
  usersById.Add(u.Id, u);
}

You can also pass a second parameter to the ToDictionary method, which is of type Func<TSource, TElement> and returns the Value to be added for each entry.

IEnumerable<User> users = GetUsers();
Dictionary<int, string> userNamesById = users.ToDictionary(x => x.Id, x => x.Name);

It is also possible to specify the IComparer that is used to compare key values. This can be useful when the key is a string and you want it to match case-insensitive.

IEnumerable<User> users = GetUsers();
Dictionary<string, User> usersByCaseInsenstiveName = users.ToDictionary(x => x.Name, StringComparer.InvariantCultureIgnoreCase);

var user1 = usersByCaseInsenstiveName["john"];
var user2 = usersByCaseInsenstiveName["JOHN"];
user1 == user2; // Returns true

Note: the ToDictionary method requires all keys to be unique, there must be no duplicate keys. If there are, then an exception is thrown: ArgumentException: An item with the same key has already been added. If you have a scenario where you know that you will have multiple elements with the same key, then you are better off using ToLookup instead.

Aggregate

Aggregate Applies an accumulator function over a sequence.

int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
  • At the first step prevSum = 1
  • At the second prevSum = prevSum(at the first step) + 2
  • At the i-th step prevSum = prevSum(at the (i-1) step) + i-th element of the array
string[] stringList = { "Hello", "World", "!" };
string joinedString = stringList.Aggregate((prev, current) => prev + " " + current);
// joinedString = "Hello World !"

A second overload of Aggregate also receives an seed parameter which is the initial accumulator value. This can be used to calculate multiple conditions on a collection without iterating it more than once.

List<int> items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

For the collection of items we want to calculate

  1. The total .Count
  2. The amount of even numbers
  3. Collect each forth item

Using Aggregate it can be done like this:

var result = items.Aggregate(new { Total = 0, Even = 0, FourthItems = new List<int>() },
                (accumelative,item) =>
                new {
                    Total = accumelative.Total + 1,
                    Even = accumelative.Even + (item % 2 == 0 ? 1 : 0),
                    FourthItems = (accumelative.Total + 1)%4 == 0 ? 
                        new List<int>(accumelative.FourthItems) { item } : 
                        accumelative.FourthItems 
                });
// Result:
// Total = 12
// Even = 6
// FourthItems = [4, 8, 12]

Note that using an anonymous type as the seed one has to instantiate a new object each item because the properties are read only. Using a custom class one can simply assign the information and no new is needed (only when giving the initial seed parameter

Defining a variable inside a Linq query (let keyword)

In order to define a variable inside a linq expression, you can use the let keyword. This is usually done in order to store the results of intermediate sub-queries, for example:

 int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

 var aboveAverages = from number in numbers
                     let average = numbers.Average()
                     let nSquared = Math.Pow(number,2)
                     where nSquared > average
                     select number;

 Console.WriteLine("The average of the numbers is {0}.", numbers.Average());

 foreach (int n in aboveAverages)
 {
   Console.WriteLine("Query result includes number {0} with square of {1}.", n, Math.Pow(n,2));
 }

Output:

The average of the numbers is 4.5.
Query result includes number 3 with square of 9.
Query result includes number 4 with square of 16.
Query result includes number 5 with square of 25.
Query result includes number 6 with square of 36.
Query result includes number 7 with square of 49.
Query result includes number 8 with square of 64.
Query result includes number 9 with square of 81.

View Demo

SkipWhile

SkipWhile() is used to exclude elements until first non-match (this might be counter intuitive to most)

int[] list = { 42, 42, 6, 6, 6, 42 };
var result = list.SkipWhile(i => i == 42); 
// Result: 6, 6, 6, 42

DefaultIfEmpty

DefaultIfEmpty is used to return a Default Element if the Sequence contains no elements. This Element can be the Default of the Type or a user defined instance of that Type. Example:

var chars = new List<string>() { "a", "b", "c", "d" };

chars.DefaultIfEmpty("N/A").FirstOrDefault(); // returns "a";

chars.Where(str => str.Length > 1)
     .DefaultIfEmpty("N/A").FirstOrDefault(); // return "N/A"

chars.Where(str => str.Length > 1)
        .DefaultIfEmpty().First(); // returns null;

Usage in Left Joins:

With DefaultIfEmpty the traditional Linq Join can return a default object if no match was found. Thus acting as a SQL's Left Join. Example:

var leftSequence = new List<int>() { 99, 100, 5, 20, 102, 105 };
var rightSequence = new List<char>() { 'a', 'b', 'c', 'i', 'd' };

var numbersAsChars = from l in leftSequence
                     join r in rightSequence
                     on l equals (int)r into leftJoin
                     from result in leftJoin.DefaultIfEmpty('?')
                     select new
                     {
                         Number = l,
                         Character = result
                     };

foreach(var item in numbersAsChars)
{
    Console.WriteLine("Num = {0} ** Char = {1}", item.Number, item.Character);
}

ouput: 

Num = 99         Char = c
Num = 100        Char = d
Num = 5          Char = ?
Num = 20         Char = ?
Num = 102        Char = ?
Num = 105        Char = i

In the case where a DefaultIfEmpty is used (without specifying a default value) and that will result will no matching items on the right sequence one must make sure that the object is not null before accessing its properties. Otherwise it will result in a NullReferenceException. Example:

var leftSequence = new List<int> { 1, 2, 5 };
var rightSequence = new List<dynamic>()
    {
        new { Value = 1 },
        new { Value = 2 },
        new { Value = 3 },
        new { Value = 4 },
    };

var numbersAsChars = (from l in leftSequence
                        join r in rightSequence
                        on l equals r.Value into leftJoin
                        from result in leftJoin.DefaultIfEmpty()
                        select new
                        {
                            Left = l,
                            // 5 will not have a matching object in the right so result 
                            // will be equal to null. 
                            // To avoid an error use:
                            //    -  C# 6.0 or above - ?. 
                            //    -  Under           - result == null ? 0 : result.Value
                            Right = result?.Value
                        }).ToList();

SequenceEqual

SequenceEqual is used to compare two IEnumerable<T> sequences with each other.

int[] a = new int[] {1, 2, 3};
int[] b = new int[] {1, 2, 3};
int[] c = new int[] {1, 3, 2};

bool returnsTrue = a.SequenceEqual(b);
bool returnsFalse = a.SequenceEqual(c);

Count and LongCount

Count returns the number of elements in an IEnumerable<T>. Count also exposes an optional predicate parameter that allows you to filter the elements you want to count.

int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };

int n = array.Count(); // returns the number of elements in the array
int x = array.Count(i => i > 2); // returns the number of elements in the array greater than 2

LongCount works the same way as Count but has a return type of long and is used for counting IEnumerable<T> sequences that are longer than int.MaxValue

int[] array = GetLargeArray();

long n = array.LongCount(); // returns the number of elements in the array
long x = array.LongCount(i => i > 100); // returns the number of elements in the array greater than 100

Incrementally building a query

Because LINQ uses deferred execution, we can have a query object that doesn't actually contain the values, but will return the values when evaluated. We can thus dynamically build the query based on our control flow, and evaluate it once we are finished:

IEnumerable<VehicleModel> BuildQuery(int vehicleType, SearchModel search, int start = 1, int count = -1) {
    IEnumerable<VehicleModel> query = _entities.Vehicles
        .Where(x => x.Active && x.Type == vehicleType)
        .Select(x => new VehicleModel {
            Id = v.Id,
            Year = v.Year,
            Class = v.Class,
            Make = v.Make,
            Model = v.Model,
            Cylinders = v.Cylinders ?? 0
        });

We can conditionally apply filters:

    if (!search.Years.Contains("all", StringComparer.OrdinalIgnoreCase))
        query = query.Where(v => search.Years.Contains(v.Year));

    if (!search.Makes.Contains("all", StringComparer.OrdinalIgnoreCase)) {
        query = query.Where(v => search.Makes.Contains(v.Make));
    }

    if (!search.Models.Contains("all", StringComparer.OrdinalIgnoreCase)) {
        query = query.Where(v => search.Models.Contains(v.Model));
    }

    if (!search.Cylinders.Equals("all", StringComparer.OrdinalIgnoreCase)) {
        decimal minCylinders = 0;
        decimal maxCylinders = 0;
        switch (search.Cylinders) {
            case "2-4":
                maxCylinders = 4;
                break;
            case "5-6":
                minCylinders = 5;
                maxCylinders = 6;
                break;
            case "8":
                minCylinders = 8;
                maxCylinders = 8;
                break;
            case "10+":
                minCylinders = 10;
                break;
        }
        if (minCylinders > 0) {
            query = query.Where(v => v.Cylinders >= minCylinders);
        }
        if (maxCylinders > 0) {
            query = query.Where(v => v.Cylinders <= maxCylinders);
        }
    }

We can add a sort order to the query based on a condition:

    switch (search.SortingColumn.ToLower()) {
        case "make_model":
            query = query.OrderBy(v => v.Make).ThenBy(v => v.Model);
            break;
        case "year":
            query = query.OrderBy(v => v.Year);
            break;
        case "engine_size":
            query = query.OrderBy(v => v.EngineSize).ThenBy(v => v.Cylinders);
            break;
        default:
            query = query.OrderBy(v => v.Year); //The default sorting.
    }

Our query can be defined to start from a given point:

    query = query.Skip(start - 1);

and defined to return a specific number of records:

    if (count > -1) {
        query = query.Take(count);
    }
    return query;
}

Once we have the query object, we can evaluate the results with a foreach loop, or one of the LINQ methods that returns a set of values, such as ToList or ToArray:

SearchModel sm;

// populate the search model here
// ...

List<VehicleModel> list = BuildQuery(5, sm).ToList();

Zip

The Zip extension method acts upon two collections. It pairs each element in the two series together based on position. With a Func instance, we use Zip to handle elements from the two C# collections in pairs. If the series differ in size, the extra elements of the larger series will be ignored.

To take an example from the book "C# in a Nutshell",

int[] numbers = { 3, 5, 7 };
string[] words = { "three", "five", "seven", "ignored" };
IEnumerable<string> zip = numbers.Zip(words, (n, w) => n + "=" + w);

Output:

3=three
5=five
7=seven

View Demo

GroupJoin with outer range variable

Customer[] customers = Customers.ToArray();
Purchase[] purchases = Purchases.ToArray();

var groupJoinQuery =
    from c in customers
    join p in purchases on c.ID equals p.CustomerID
    into custPurchases
    select new
    {
        CustName = c.Name,
        custPurchases
    };

ElementAt and ElementAtOrDefault

ElementAt will return the item at index n. If n is not within the range of the enumerable, throws an ArgumentOutOfRangeException.

int[] numbers  = { 1, 2, 3, 4, 5 };
numbers.ElementAt(2);  // 3
numbers.ElementAt(10); // throws ArgumentOutOfRangeException

ElementAtOrDefault will return the item at index n. If n is not within the range of the enumerable, returns a default(T).

int[] numbers  = { 1, 2, 3, 4, 5 };
numbers.ElementAtOrDefault(2);  // 3
numbers.ElementAtOrDefault(10); // 0 = default(int)

Both ElementAt and ElementAtOrDefault are optimized for when the source is an IList<T> and normal indexing will be used in those cases.

Note that for ElementAt, if the provided index is greater than the size of the IList<T>, the list should (but is technically not guaranteed to) throw an ArgumentOutOfRangeException.

Linq Quantifiers

Quantifier operations return a Boolean value if some or all of the elements in a sequence satisfy a condition. In this article, we will see some common LINQ to Objects scenarios where we can use these operators. There are 3 Quantifiers operations that can be used in LINQ:

All – used to determine whether all the elements in a sequence satisfy a condition. Eg:

int[] array = { 10, 20, 30 }; 
   
// Are all elements >= 10? YES
array.All(element => element >= 10); 
   
// Are all elements >= 20? NO
array.All(element => element >= 20);
    
// Are all elements < 40? YES
array.All(element => element < 40);

Any - used to determine whether any elements in a sequence satisfy a condition. Eg:

int[] query=new int[] { 2, 3, 4 }
query.Any (n => n == 3);

Contains - used to determine whether a sequence contains a specified element. Eg:

//for int array
int[] query =new int[] { 1,2,3 };
query.Contains(1);

//for string array
string[] query={"Tom","grey"};
query.Contains("Tom");

//for a string
var stringValue="hello";
stringValue.Contains("h");

Joining multiple sequences

Consider entities Customer, Purchase and PurchaseItem as follows:

public class Customer
{
   public string Id { get; set } // A unique Id that identifies customer    
   public string Name  {get; set; }
}

public class Purchase
{
   public string Id { get; set }
   public string CustomerId {get; set; }
   public string Description { get; set; }
}

public class PurchaseItem
{
   public string Id { get; set }
   public string PurchaseId {get; set; }
   public string Detail { get; set; }
}

Consider following sample data for above entities:

var customers = new List<Customer>()             
 {
    new Customer() {
        Id = Guid.NewGuid().ToString(),
        Name = "Customer1"            
    },
            
    new Customer() {
        Id = Guid.NewGuid().ToString(),
        Name = "Customer2"            
    }
 };        
    
 var purchases = new List<Purchase>() 
 {
     new Purchase() {                
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[0].Id,
         Description = "Customer1-Purchase1"            
     },

     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[0].Id,
         Description = "Customer1-Purchase2"            
     },
     
     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[1].Id,
         Description = "Customer2-Purchase1"            
     },

     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[1].Id,
         Description = "Customer2-Purchase2"            
     }
  };
    
 var purchaseItems = new List<PurchaseItem>() 
 {
     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[0].Id,
         Detail = "Purchase1-PurchaseItem1"            
     },

     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[1].Id,
         Detail = "Purchase2-PurchaseItem1"            
     },
     
     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[1].Id,
         Detail = "Purchase2-PurchaseItem2"            
     },

     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[3].Id,
         Detail = "Purchase3-PurchaseItem1"
     }
 };

Now, consider below linq query:

var result = from c in customers
            join p in purchases on c.Id equals p.CustomerId           // first join
            join pi in purchaseItems on p.Id equals pi.PurchaseId     // second join
            select new
            {
               c.Name, p.Description, pi.Detail
            };

To output the result of above query:

foreach(var resultItem in result)
{
    Console.WriteLine($"{resultItem.Name}, {resultItem.Description}, {resultItem.Detail}");
}

The output of the query would be:

Customer1, Customer1-Purchase1, Purchase1-PurchaseItem1

Customer1, Customer1-Purchase2, Purchase2-PurchaseItem1

Customer1, Customer1-Purchase2, Purchase2-PurchaseItem2

Customer2, Customer2-Purchase2, Purchase3-PurchaseItem1

Live Demo on .NET Fiddle

Joining on multiple keys

  PropertyInfo[] stringProps = typeof (string).GetProperties();//string properties
  PropertyInfo[] builderProps = typeof(StringBuilder).GetProperties();//stringbuilder properties
    
    var query =
        from s in stringProps
        join b in builderProps
            on new { s.Name, s.PropertyType } equals new { b.Name, b.PropertyType }
        select new
        {
            s.Name,
            s.PropertyType,
            StringToken = s.MetadataToken,
            StringBuilderToken = b.MetadataToken
        };

Note that anonymous types in above join must contain same properties since objects are considered equal only if all their properties are equal. Otherwise query won't compile.

Select with Func selector - Use to get ranking of elements

On of the overloads of the Select extension methods also passes the index of the current item in the collection being selected. These are a few uses of it.

Get the "row number" of the items

var rowNumbers = collection.OrderBy(item => item.Property1)
                           .ThenBy(item => item.Property2)
                           .ThenByDescending(item => item.Property3)
                           .Select((item, index) => new { Item = item, RowNumber = index })
                           .ToList();

Get the rank of an item within its group

var rankInGroup = collection.GroupBy(item => item.Property1)
                            .OrderBy(group => group.Key)
                            .SelectMany(group => group.OrderBy(item => item.Property2)
                                                   .ThenByDescending(item => item.Property3)
                                                   .Select((item, index) => new 
                                                   { 
                                                       Item = item, 
                                                       RankInGroup = index 
                                                   })).ToList();

Get the ranking of groups (also known in Oracle as dense_rank)

var rankOfBelongingGroup = collection.GroupBy(item => item.Property1)
                            .OrderBy(group => group.Key)
                            .Select((group, index) => new
                            {
                                Items = group,
                                Rank = index
                            })
                            .SelectMany(v => v.Items, (s, i) => new
                            {
                                Item = i,
                                DenseRank = s.Rank
                            }).ToList();

For testing this you can use:

public class SomeObject
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
    public int Property3 { get; set; }

    public override string ToString()
    {
        return string.Join(", ", Property1, Property2, Property3);
    }
}

And data:

List<SomeObject> collection = new List<SomeObject>
{
    new SomeObject { Property1 = 1, Property2 = 1, Property3 = 1},
    new SomeObject { Property1 = 1, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 1, Property2 = 2, Property3 = 2},
    new SomeObject { Property1 = 2, Property2 = 1, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 3, Property3 = 1}
};

TakeWhile

TakeWhile returns elements from a sequence as long as the condition is true

int[] list = { 1, 10, 40, 50, 44, 70, 4 };
var result = list.TakeWhile(item => item < 50).ToList();
// result = { 1, 10, 40 }

Sum

The Enumerable.Sum extension method calculates the sum of numeric values.

In case the collection's elements are themselves numbers, you can calculate the sum directly.

int[] numbers = new int[] { 1, 4, 6 };
Console.WriteLine( numbers.Sum() ); //outputs 11

In case the type of the elements is a complex type, you can use a lambda expression to specify the value that should be calculated:

var totalMonthlySalary = employees.Sum( employee => employee.MonthlySalary );

Sum extension method can calculate with the following types:

  • Int32
  • Int64
  • Single
  • Double
  • Decimal

In case your collection contains nullable types, you can use the null-coalescing operator to set a default value for null elements:

int?[] numbers = new int?[] { 1, null, 6 };
Console.WriteLine( numbers.Sum( number => number ?? 0 ) ); //outputs 7

ToLookup

ToLookup returns a data structure that allows indexing. It is an extension method. It produces an ILookup instance that can be indexed or enumerated using a foreach-loop. The entries are combined into groupings at each key. - dotnetperls

string[] array = { "one", "two", "three" };
//create lookup using string length as key
var lookup = array.ToLookup(item => item.Length);

//join the values whose lengths are 3
Console.WriteLine(string.Join(",",lookup[3]));
//output: one,two

Another Example:

int[] array = { 1,2,3,4,5,6,7,8 };
//generate lookup for odd even numbers (keys will be 0 and 1)
var lookup = array.ToLookup(item => item % 2);

//print even numbers after joining
Console.WriteLine(string.Join(",",lookup[0]));
//output: 2,4,6,8

//print odd numbers after joining
Console.WriteLine(string.Join(",",lookup[1]));
//output: 1,3,5,7

Build your own Linq operators for IEnumerable

One of the great things about Linq is that it is so easy to extend. You just need to create an extension method whose argument is IEnumerable<T>.

public namespace MyNamespace
{
    public static class LinqExtensions
    {
        public static IEnumerable<List<T>> Batch<T>(this IEnumerable<T> source, int batchSize)
        {
            var batch = new List<T>();
            foreach (T item in source)
            {
                batch.Add(item);
                if (batch.Count == batchSize)
                {
                    yield return batch;
                    batch = new List<T>();
                }
            }
            if (batch.Count > 0)
                yield return batch;
        }
    }
}

This example splits the items in an IEnumerable<T> into lists of a fixed size, the last list containing the remainder of the items. Notice how the object to which the extension method is applied is passed in (argument source) as the initial argument using the this keyword. Then the yield keyword is used to output the next item in the output IEnumerable<T> before continuing with execution from that point (see yield keyword).

This example would be used in your code like this:

//using MyNamespace;
var items = new List<int> { 2, 3, 4, 5, 6 };
foreach (List<int> sublist in items.Batch(3))
{
    // do something
}

On the first loop, sublist would be {2, 3, 4} and on the second {5, 6}.

Custom LinQ methods can be combined with standard LinQ methods too. e.g.:

//using MyNamespace;
var result = Enumerable.Range(0, 13)         // generate a list
                       .Where(x => x%2 == 0) // filter the list or do something other
                       .Batch(3)             // call our extension method
                       .ToList()             // call other standard methods

This query will return even numbers grouped in batches with a size of 3: {0, 2, 4}, {6, 8, 10}, {12}

Remember you need a using MyNamespace; line in order to be able to access the extension method.

Using SelectMany instead of nested loops

Given 2 lists

var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "1", "2", "3", "4" };

if you want to output all permutations you could use nested loops like

var result = new List<string>();
foreach (var s1 in list1)
    foreach (var s2 in list2)
        result.Add($"{s1}{s2}");

Using SelectMany you can do the same operation as

var result = list1.SelectMany(x => list2.Select(y => $"{x}{y}", x, y)).ToList();

Any and First(OrDefault) - best practice

I won't explain what Any and FirstOrDefault does because there are already two good example about them. See Any and First, FirstOrDefault, Last, LastOrDefault, Single, and SingleOrDefault for more information.

A pattern I often see in code which should be avoided is

if (myEnumerable.Any(t=>t.Foo == "Bob"))
{
    var myFoo = myEnumerable.First(t=>t.Foo == "Bob");
    //Do stuff
}

It could be written more efficiently like this

var myFoo = myEnumerable.FirstOrDefault(t=>t.Foo == "Bob");
if (myFoo != null)
{
    //Do stuff
}

By using the second example, the collection is searched only once and give the same result as the first one. The same idea can be applied to Single.

GroupBy Sum and Count

Let's take a sample class:

public class Transaction
{
    public string Category { get; set; }
    public DateTime Date { get; set; }
    public decimal Amount { get; set; }
}

Now, let us consider a list of transactions:

var transactions = new List<Transaction>
{
   new Transaction { Category = "Saving Account", Amount = 56, Date = DateTime.Today.AddDays(1) },
   new Transaction { Category = "Saving Account", Amount = 10, Date = DateTime.Today.AddDays(-10) },
   new Transaction { Category = "Credit Card", Amount = 15, Date = DateTime.Today.AddDays(1) },
   new Transaction { Category = "Credit Card", Amount = 56, Date = DateTime.Today },
   new Transaction { Category = "Current Account", Amount = 100, Date = DateTime.Today.AddDays(5) },
};

If you want to calculate category wise sum of amount and count, you can use GroupBy as follows:

var summaryApproach1 = transactions.GroupBy(t => t.Category)
                           .Select(t => new
                           {
                               Category = t.Key,
                               Count = t.Count(),
                               Amount = t.Sum(ta => ta.Amount),
                           }).ToList();

Console.WriteLine("-- Summary: Approach 1 --");
summaryApproach1.ForEach(
            row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));

Alternatively, you can do this in one step:

var summaryApproach2 = transactions.GroupBy(t => t.Category, (key, t) =>
{
        var transactionArray = t as Transaction[] ?? t.ToArray();
        return new
        {
            Category = key,
            Count = transactionArray.Length,
            Amount = transactionArray.Sum(ta => ta.Amount),
        };
}).ToList();

Console.WriteLine("-- Summary: Approach 2 --");
summaryApproach2.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));

Output for both the above queries would be same:

Category: Saving Account, Amount: 66, Count: 2

Category: Credit Card, Amount: 71, Count: 2

Category: Current Account, Amount: 100, Count: 1

Live Demo in .NET Fiddle

Reverse

  • Inverts the order of the elements in a sequence.
  • If there is no items throws a ArgumentNullException: source is null.

Example:

// Create an array.
int[] array = { 1, 2, 3, 4 };                         //Output:
// Call reverse extension method on the array.        //4
var reverse = array.Reverse();                        //3
// Write contents of array to screen.                 //2
foreach (int value in reverse)                        //1
    Console.WriteLine(value);

Live code example

Remeber that Reverse() may work diffrent depending on the chain order of your LINQ statements.

        //Create List of chars
        List<int> integerlist = new List<int>() { 1, 2, 3, 4, 5, 6 };

        //Reversing the list then taking the two first elements
        IEnumerable<int> reverseFirst = integerlist.Reverse<int>().Take(2);
        
        //Taking 2 elements and then reversing only thos two
        IEnumerable<int> reverseLast = integerlist.Take(2).Reverse();
        
        //reverseFirst output: 6, 5
        //reverseLast output:  2, 1

Live code example

Reverse() works by buffering everything then walk through it backwards, whitch is not very efficient, but neither is OrderBy from that perspective.

In LINQ-to-Objects, there are buffering operations (Reverse, OrderBy, GroupBy, etc) and non-buffering operations (Where, Take, Skip, etc).

Example: Non-buffering Reverse extention

public static IEnumerable<T> Reverse<T>(this IList<T> list) {
    for (int i = list.Count - 1; i >= 0; i--) 
        yield return list[i];
}

Live code example

This method can encounter problems if u mutate the list while iterating.

Enumerating the Enumerable

The IEnumerable<T> interface is the base interface for all generic enumerators and is a quintessential part of understanding LINQ. At its core, it represents the sequence.

This underlying interface is inherited by all of the generic collections, such as Collection<T>, Array, List<T>, Dictionary<TKey, TValue> Class, and HashSet<T>.

In addition to representing the sequence, any class that inherits from IEnumerable<T> must provide an IEnumerator<T>. The enumerator exposes the iterator for the enumerable, and these two interconnected interfaces and ideas are the source of the saying "enumerate the enumerable".

"Enumerating the enumerable" is an important phrase. The enumerable is simply a structure for how to iterate, it does not hold any materialized objects. For example, when sorting, an enumerable may hold the criteria of the field to sort, but using .OrderBy() in itself will return an IEnumerable<T> which only knows how to sort. Using a call which will materialize the objects, as in iterate the set, is known as enumerating (for example .ToList()). The enumeration process will use the the enumerable definition of how in order to move through the series and return the relevant objects (in order, filtered, projected, etc.).

Only once the enumerable has been enumerated does it cause the materialization of the objects, which is when metrics like time complexity (how long it should take related to series size) and spacial complexity (how much space it should use related to series size) can be measured.

Creating your own class that inherits from IEnumerable<T> can be a little complicated depending on the underlying series that needs to be enumerable. In general it is best to use one of the existing generic collections. That said, it is also possible to inherit from the IEnumerable<T> interface without having a defined array as the underlying structure.

For example, using the Fibonacci series as the underlying sequence. Note that the call to Where simply builds an IEnumerable, and it is not until a call to enumerate that enumerable is made that any of the values are materialized.

void Main()
{
    Fibonacci Fibo = new Fibonacci();
    IEnumerable<long> quadrillionplus = Fibo.Where(i => i > 1000000000000);
    Console.WriteLine("Enumerable built");
    Console.WriteLine(quadrillionplus.Take(2).Sum());
    Console.WriteLine(quadrillionplus.Skip(2).First());

    IEnumerable<long> fibMod612 = Fibo.OrderBy(i => i % 612);
    Console.WriteLine("Enumerable built");
    Console.WriteLine(fibMod612.First());//smallest divisible by 612
}

public class Fibonacci : IEnumerable<long>
{
    private int max = 90;

    //Enumerator called typically from foreach
    public IEnumerator GetEnumerator() {
        long n0 = 1;
        long n1 = 1;
        Console.WriteLine("Enumerating the Enumerable");
        for(int i = 0; i < max; i++){
            yield return n0+n1;
            n1 += n0;
            n0 = n1-n0;
        }
    }
    
    //Enumerable called typically from linq
    IEnumerator<long> IEnumerable<long>.GetEnumerator() {
        long n0 = 1;
        long n1 = 1;
        Console.WriteLine("Enumerating the Enumerable");
        for(int i = 0; i < max; i++){
            yield return n0+n1;
            n1 += n0;
            n0 = n1-n0;
        }
    }
}

Output

Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352

The strength in the second set (the fibMod612) is that even though we made the call to order our entire set of Fibonacci numbers, since only one value was taken using .First() the time complexity was O(n) as only 1 value needed to be compared during the ordering algorithm's execution. This is because our enumerator only asked for 1 value, and so the entire enumerable did not have to be materialized. Had we used .Take(5) instead of .First() the enumerator would have asked for 5 values, and at most 5 values would need to be materialized. Compared to needing to order an entire set and then take the first 5 values, the principle of saves a lot of execution time and space.

OrderBy

Orders a collection by a specified value.

When the value is an integer, double or float it starts with the minimum value, which means that you get first the negative values, than zero and afterwords the positive values (see Example 1).

When you order by a char the method compares the ascii values of the chars to sort the collection (see Example 2).

When you sort strings the OrderBy method compares them by taking a look at their CultureInfo but normaly starting with the first letter in the alphabet (a,b,c...).

This kind of order is called ascending, if you want it the other way round you need descending (see OrderByDescending).

Example 1:

int[] numbers = {2, 1, 0, -1, -2};
IEnumerable<int> ascending = numbers.OrderBy(x => x);
// returns {-2, -1, 0, 1, 2}

Example 2:

 char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
 IEnumerable<char> ascending = letters.OrderBy(x => x);
 // returns { ' ', '!', '+', '1', '9', '?', 'A', 'B', 'Y', 'Z', '[', 'a', 'b', 'y', 'z', '{' }

Example:

class Person
{
   public string Name { get; set; }
   public int Age { get; set; }
}

var people = new[]
{
    new Person {Name = "Alice", Age = 25},
    new Person {Name = "Bob", Age = 21},
    new Person {Name = "Carol", Age = 43}
};
var youngestPerson = people.OrderBy(x => x.Age).First();
var name = youngestPerson.Name; // Bob

OrderByDescending

Orders a collection by a specified value.

When the value is an integer, double or float it starts with the maximal value, which means that you get first the positive values, than zero and afterwords the negative values (see Example 1).

When you order by a char the method compares the ascii values of the chars to sort the collection (see Example 2).

When you sort strings the OrderBy method compares them by taking a look at their CultureInfo but normaly starting with the last letter in the alphabet (z,y,x,...).

This kind of order is called descending, if you want it the other way round you need ascending (see OrderBy).

Example 1:

int[] numbers = {-2, -1, 0, 1, 2};
IEnumerable<int> descending = numbers.OrderByDescending(x => x);
// returns {2, 1, 0, -1, -2}

Example 2:

char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
IEnumerable<char> descending = letters.OrderByDescending(x => x);
// returns { '{', 'z', 'y', 'b', 'a', '[', 'Z', 'Y', 'B', 'A', '?', '9', '1', '+', '!', ' ' }

Example 3:

class Person
{
   public  string Name { get; set; }
   public  int Age { get; set; }
}

var people = new[]
{
    new Person {Name = "Alice", Age = 25},
    new Person {Name = "Bob", Age = 21},
    new Person {Name = "Carol", Age = 43}
};
var oldestPerson = people.OrderByDescending(x => x.Age).First();
var name = oldestPerson.Name; // Carol

Concat

Merges two collections (without removing duplicates)

List<int> foo = new List<int> { 1, 2, 3 };
List<int> bar = new List<int> { 3, 4, 5 };

// Through Enumerable static class
var result = Enumerable.Concat(foo, bar).ToList(); // 1,2,3,3,4,5

// Through extension method
var result = foo.Concat(bar).ToList(); // 1,2,3,3,4,5

Contains

MSDN:

Determines whether a sequence contains a specified element by using a specified IEqualityComparer<T>

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var result1 = numbers.Contains(4); // true
var result2 = numbers.Contains(8); // false

List<int> secondNumberCollection = new List<int> { 4, 5, 6, 7 };
// Note that can use the Intersect method in this case
var result3 = secondNumberCollection.Where(item => numbers.Contains(item)); // will be true only for 4,5

Using a user defined object:

public class Person
{
   public string Name { get; set; }
}

List<Person> objects = new List<Person>
{
    new Person { Name = "Nikki"},
    new Person { Name = "Gilad"},
    new Person { Name = "Phil"},
    new Person { Name = "John"}
};

//Using the Person's Equals method - override Equals() and GetHashCode() - otherwise it
//will compare by reference and result will be false
var result4 = objects.Contains(new Person { Name = "Phil" }); // true

Using the Enumerable.Contains(value, comparer) overload:

public class Compare : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name;
    }
    public int GetHashCode(Person codeh)
    {
        return codeh.Name.GetHashCode();
    }
}

var result5 = objects.Contains(new Person { Name = "Phil" }, new Compare()); // true

A smart usage of Contains would be to replace multiple if clauses to a Contains call.

So instead of doing this:

if(status == 1 || status == 3 || status == 4)
{
    //Do some business operation
}
else
{
    //Do something else
}

Do this:

if(new int[] {1, 3, 4 }.Contains(status)
{
    //Do some business operaion
}
else 
{
    //Do something else
}