C#idiomatic C#
standard-query-operatorscsharp/linq

LINQ

Standard query operators overview

The LINQ standard query operators provide query capabilities including filtering, projection, aggregation, and sorting in C#.

  • page csharp/linq/standard-query-operators
  • updated 12/01/2025

The standard query operators are the keywords and methods that form the LINQ pattern. The C# language defines LINQ query keywords that you use for the most common query expressions. The compiler translates expressions using these keywords to the equivalent method calls. The two forms are synonymous. Other methods that are part of the System.Linq namespace don't have equivalent query keywords. In those cases, you must use the method syntax. This section covers all the query operator keywords. The runtime and other NuGet packages add more methods designed to work with LINQ queries each release. The most common methods, including those that have query keyword equivalents, are covered in this section. For the full list of query methods supported by the .NET Runtime, see the System.Linq.Enumerable API documentation. In addition to the methods covered here, this class contains methods for concatenating data sources, computing a single value from a data source, such as a sum, average, or other value.


author: BillWagner ms.author: wiwagn ms.topic: include ms.date: 05/29/2024

Most of these methods operate on sequences, where a sequence is an object whose type implements the System.Collections.Generic.IEnumerable interface or the System.Linq.IQueryable interface. The standard query operators provide query capabilities including filtering, projection, aggregation, sorting, and more. The methods that make up each set are extension members defined in the System.Linq.Enumerable and System.Linq.Queryable classes, respectively. They're defined as *extension members where the receiver type is either the IEnumerable<T> or IQueryable<T> type that they operate on.

The distinction between System.Collections.Generic.IEnumerable and System.Linq.IQueryable sequences determines how the query is executed at runtime.

For IEnumerable<T>, the returned enumerable object captures the arguments that were passed to the method. When that object is enumerated, the logic of the query operator is employed and the query results are returned.

For IQueryable<T>, the query is translated into an expression tree. The expression tree can be translated to a native query when the data source can optimize the query. Libraries such as Entity Framework translate LINQ queries into native SQL queries that execute at the database.

The following code example demonstrates how you can use the standard query operators to obtain information about a sequence.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/IndexExamples.csFirstSentence

csharp
using System.Xml.Linq;

namespace StandardQueryOperators;

internal class IndexExamples
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Sentences();
        PartsOfAQuery();
        XmlTransformation();
    }

    private static void Sentences()
    {
        // <FirstSentence>
        string sentence = "the quick brown fox jumps over the lazy dog";
        // Split the string into individual words to create a collection.
        string[] words = sentence.Split(' ');

        // Using query expression syntax.
        var query = from word in words
                    group word.ToUpper() by word.Length into gr
                    orderby gr.Key
                    select new { Length = gr.Key, Words = gr };

        // Using method-based query syntax.
        var query2 = words.
            GroupBy(w => w.Length, w => w.ToUpper()).
            Select(g => new { Length = g.Key, Words = g }).
            OrderBy(o => o.Length);

        foreach (var obj in query)
        {
            Console.WriteLine($"Words of length {obj.Length}:");
            foreach (string word in obj.Words)
                Console.WriteLine(word);
        }

        // This code example produces the following output:
        //
        // Words of length 3:
        // THE
        // FOX
        // THE
        // DOG
        // Words of length 4:
        // OVER
        // LAZY
        // Words of length 5:
        // QUICK
        // BROWN
        // JUMPS
        // </FirstSentence>
    }

    private static void PartsOfAQuery()
    {
        //<ObtainDataSource>
        //queryAllStudents is an IEnumerable<Student>
        var queryAllStudents = from student in students
                                select student;
        //</ObtainDataSource>
    }

    private static void XmlTransformation()
    {
        // <XmlTransformation>

        // Create the query.
        var studentsToXML = new XElement("Root",
            from student in students
            let scores = string.Join(",", student.Scores)
            select new XElement("student",
                        new XElement("First", student.FirstName),
                        new XElement("Last", student.LastName),
                        new XElement("Scores", scores)
                    ) // end "student"
                ); // end "Root"

        // Execute the query.
        Console.WriteLine(studentsToXML);
        // </XmlTransformation>
        /* Output:
          // <XmlTransformationOutput>
          <Root>
            <student>
              <First>Svetlana</First>
              <Last>Omelchenko</Last>
              <Scores>97,90,73,54</Scores>
            </student>
            <student>
              <First>Claire</First>
              <Last>O'Donnell</Last>
              <Scores>56,78,95,95</Scores>
            </student>
            ...
            <student>
              <First>Max</First>
              <Last>Lindgren</Last>
              <Scores>86,88,96,63</Scores>
            </student>
            <student>
              <First>Arina</First>
              <Last>Ivanova</Last>
              <Scores>93,63,70,80</Scores>
            </student>
          </Root>
          // </XmlTransformationOutput>
        */
    }
}

Where possible, the queries in this section use a sequence of words or numbers as the input source. For queries where more complicated relationships between objects are used, the following sources that model a school are used:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/DataSources.csQueryDataSource

csharp
namespace StandardQueryOperators;

//<QueryDataSource>
public enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

public class Student
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required int ID { get; init; }

    public required GradeLevel Year { get; init; }
    public required List<int> Scores { get; init; }

    public required int DepartmentID { get; init; }
}

public class Teacher
{
    public required string First { get; init; }
    public required string Last { get; init; }
    public required int ID { get; init; }
    public required string City { get; init; }
}

public class Department
{
    public required string Name { get; init; }
    public int ID { get; init; }

    public required int TeacherID { get; init; }
}
// </QueryDataSource>

public static class Sources
{
    public static IEnumerable<Department> Departments =>
    [
        new() { Name = "English",     ID = 1, TeacherID = 901 },
        new() { Name = "Mathematics", ID = 2, TeacherID = 965 },
        new() { Name = "Engineering", ID = 3, TeacherID = 932 },
        new() { Name = "Economics",   ID = 4, TeacherID = 945 },
        new() { Name = "Physics",     ID = 5, TeacherID = 987 },
        new() { Name = "Chemistry",   ID = 6, TeacherID = 901 }
    ];

    // Create a data source by using a collection initializer.
    public static IEnumerable<Student> Students =>
    [
        new() { FirstName = "Svetlana",   LastName = "Omelchenko",  DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 111, Scores = [97, 90, 73, 54] },
        new() { FirstName = "Claire",     LastName = "O'Donnell",   DepartmentID = -1, Year = GradeLevel.FirstYear,  ID = 112, Scores = [56, 78, 95, 95] },
        new() { FirstName = "Sven",       LastName = "Mortensen",   DepartmentID =  3, Year = GradeLevel.SecondYear, ID = 113, Scores = [61, 52, 48, 72] },
        new() { FirstName = "Cesar",      LastName = "Garcia",      DepartmentID =  4, Year = GradeLevel.SecondYear, ID = 114, Scores = [71, 86, 77, 97] },
        new() { FirstName = "Debra",      LastName = "Garcia",      DepartmentID =  5, Year = GradeLevel.ThirdYear,  ID = 115, Scores = [66, 96, 70, 69] },
        new() { FirstName = "Fadi",       LastName = "Fakhouri",    DepartmentID =  6, Year = GradeLevel.ThirdYear,  ID = 116, Scores = [93, 72, 62, 65] },
        new() { FirstName = "Hanying",    LastName = "Feng",        DepartmentID =  1, Year = GradeLevel.FourthYear, ID = 117, Scores = [53, 81, 81, 50] },
        new() { FirstName = "Hugo",       LastName = "Garcia",      DepartmentID =  2, Year = GradeLevel.FourthYear, ID = 118, Scores = [68, 91, 60, 51] },
        new() { FirstName = "Lance",      LastName = "Tucker",      DepartmentID = -1, Year = GradeLevel.FirstYear,  ID = 119, Scores = [83, 42, 68, 63] },
        new() { FirstName = "Terry",      LastName = "Adams",       DepartmentID =  4, Year = GradeLevel.SecondYear, ID = 120, Scores = [63, 91, 71, 51] },
        new() { FirstName = "Eugene",     LastName = "Zabokritski", DepartmentID =  5, Year = GradeLevel.ThirdYear,  ID = 121, Scores = [56, 40, 73, 75] },
        new() { FirstName = "Michael",    LastName = "Tucker",      DepartmentID =  3, Year = GradeLevel.FourthYear, ID = 122, Scores = [85, 82, 81, 70] },
        new() { FirstName = "Mark",       LastName = "Johansson",   DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 123, Scores = [82, 94, 84, 65] },
        new() { FirstName = "Sarah",      LastName = "Andersson",   DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 124, Scores = [77, 83, 67, 90] },
        new() { FirstName = "Christiane", LastName = "Jensen",      DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 125, Scores = [88, 57, 65, 87] },
        new() { FirstName = "Evgeniya",   LastName = "Maslova",     DepartmentID =  2, Year = GradeLevel.SecondYear, ID = 126, Scores = [46, 84, 87, 66] },
        new() { FirstName = "Innocenty",  LastName = "Popov",       DepartmentID =  6, Year = GradeLevel.SecondYear, ID = 127, Scores = [92, 45, 88, 60] },
        new() { FirstName = "Anna",       LastName = "Hedlund",     DepartmentID =  2, Year = GradeLevel.SecondYear, ID = 128, Scores = [85, 54, 74, 75] },
        new() { FirstName = "Erik",       LastName = "Axelsson",    DepartmentID =  3, Year = GradeLevel.ThirdYear,  ID = 129, Scores = [51, 78, 54, 49] },
        new() { FirstName = "Anna",       LastName = "Yermolayeva", DepartmentID =  3, Year = GradeLevel.ThirdYear,  ID = 130, Scores = [46, 73, 93, 68] },
        new() { FirstName = "Cassie",     LastName = "Hicks",       DepartmentID =  3, Year = GradeLevel.ThirdYear,  ID = 131, Scores = [85, 60, 85, 56] },
        new() { FirstName = "Ifeanacho",  LastName = "Jamuike",     DepartmentID =  4, Year = GradeLevel.FourthYear, ID = 132, Scores = [54, 98, 56, 61] },
        new() { FirstName = "Carmen",     LastName = "Vella",       DepartmentID =  6, Year = GradeLevel.FourthYear, ID = 133, Scores = [63, 57, 69, 70] },
        new() { FirstName = "Noel",       LastName = "Svensson",    DepartmentID =  4, Year = GradeLevel.FourthYear, ID = 134, Scores = [85, 60, 80, 73] },
        new() { FirstName = "Sanna",      LastName = "Hansson",     DepartmentID =  5, Year = GradeLevel.FirstYear,  ID = 135, Scores = [46, 94, 93, 45] },
        new() { FirstName = "Sofiya",     LastName = "Seleznyova",  DepartmentID =  6, Year = GradeLevel.FirstYear,  ID = 136, Scores = [74, 45, 87, 55] },
        new() { FirstName = "Amy E.",     LastName = "Edwards",     DepartmentID =  5, Year = GradeLevel.FirstYear,  ID = 137, Scores = [87, 59, 55, 70] },
        new() { FirstName = "Nancy",      LastName = "Engström",    DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 138, Scores = [75, 73, 78, 83] },
        new() { FirstName = "Katerina",   LastName = "Kovaleva",    DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 139, Scores = [44, 50, 47, 41] },
        new() { FirstName = "Ifunanya",   LastName = "Ugomma",      DepartmentID =  4, Year = GradeLevel.FirstYear,  ID = 140, Scores = [84, 82, 96, 80] },
        new() { FirstName = "Don",        LastName = "Richardson",  DepartmentID =  5, Year = GradeLevel.FirstYear,  ID = 141, Scores = [47, 91, 73, 68] },
        new() { FirstName = "Josephine",  LastName = "Balzan",      DepartmentID =  6, Year = GradeLevel.SecondYear, ID = 142, Scores = [40, 47, 63, 42] },
        new() { FirstName = "Michelle",   LastName = "Caruana",     DepartmentID = -1, Year = GradeLevel.FirstYear,  ID = 143, Scores = [97, 92, 69, 77] },
        new() { FirstName = "Gaby",       LastName = "Frost",       DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 144, Scores = [70, 79, 47, 79] },
        new() { FirstName = "Erna",       LastName = "Nilsson",     DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 145, Scores = [56, 52, 51, 51] },
        new() { FirstName = "Naima",      LastName = "Larsson",     DepartmentID =  4, Year = GradeLevel.ThirdYear,  ID = 146, Scores = [65, 81, 44, 61] },
        new() { FirstName = "Donald",     LastName = "Urquhart",    DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 147, Scores = [92, 90, 95, 57] },
        new() { FirstName = "Nadezhda",   LastName = "Kolpakova",   DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 148, Scores = [94, 69, 52, 58] },
        new() { FirstName = "Ruth",       LastName = "Olsson",      DepartmentID =  2, Year = GradeLevel.FourthYear, ID = 149, Scores = [66, 49, 82, 74] },
        new() { FirstName = "Maria",      LastName = "Sammut",      DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 150, Scores = [43, 83, 94, 60] },
        new() { FirstName = "Veronika",   LastName = "Berg",        DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 151, Scores = [59, 76, 65, 92] },
        new() { FirstName = "Nicholas",   LastName = "Micallef",    DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 152, Scores = [44, 57, 52, 63] },
        new() { FirstName = "Izuchukwu",  LastName = "Adaobi",      DepartmentID =  4, Year = GradeLevel.SecondYear, ID = 153, Scores = [51, 40, 42, 54] },
        new() { FirstName = "Nwanneka",   LastName = "Ifeoma",      DepartmentID =  4, Year = GradeLevel.FirstYear,  ID = 154, Scores = [44, 70, 98, 56] },
        new() { FirstName = "John",       LastName = "Falzon",      DepartmentID =  5, Year = GradeLevel.FirstYear,  ID = 155, Scores = [77, 65, 83, 45] },
        new() { FirstName = "Martina",    LastName = "Mattsson",    DepartmentID =  5, Year = GradeLevel.ThirdYear,  ID = 156, Scores = [51, 49, 96, 72] },
        new() { FirstName = "Jeanette",   LastName = "Berggren",    DepartmentID =  4, Year = GradeLevel.FirstYear,  ID = 157, Scores = [41, 67, 46, 68] },
        new() { FirstName = "Anastasiya", LastName = "Sazonova",    DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 158, Scores = [53, 96, 76, 49] },
        new() { FirstName = "Bruce",      LastName = "Keever",      DepartmentID =  3, Year = GradeLevel.FirstYear,  ID = 159, Scores = [54, 81, 84, 81] },
        new() { FirstName = "Sami",       LastName = "Åkesson",     DepartmentID =  5, Year = GradeLevel.FourthYear, ID = 160, Scores = [45, 85, 79, 94] },
        new() { FirstName = "Jesper",     LastName = "Jakobsson",   DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 161, Scores = [59, 98, 47, 92] },
        new() { FirstName = "Max",        LastName = "Lindgren",    DepartmentID =  2, Year = GradeLevel.FirstYear,  ID = 162, Scores = [86, 88, 96, 63] },
        new() { FirstName = "Arina",      LastName = "Ivanova",     DepartmentID =  1, Year = GradeLevel.FirstYear,  ID = 163, Scores = [93, 63, 70, 80] }
    ];

    public static IEnumerable<Teacher> Teachers =>
    [
        new() { First = "Ann",       Last = "Beebe",     ID = 901, City = "Seattle" },
        new() { First = "Alex",      Last = "Robinson",  ID = 910, City = "Redmond" },
        new() { First = "Michiyo",   Last = "Sato",      ID = 921, City = "Tacoma"  },
        new() { First = "Aylin",     Last = "Lundgren",  ID = 932, City = "Seattle" },
        new() { First = "Gleb",      Last = "Seleznyov", ID = 943, City = "Redmond" },
        new() { First = "Alexandra", Last = "Voronova",  ID = 954, City = "Tacoma" },
        new() { First = "Kevin",     Last = "McDowell",  ID = 965, City = "Redmond" },
        new() { First = "Selma",     Last = "Nyberg",    ID = 976, City = "Seattle" },
        new() { First = "Selma",     Last = "Åstrom",    ID = 987, City = "Redmond" },
        new() { First = "Hillevi",   Last = "Bengtsson", ID = 998, City = "Tacoma" },
        new() { First = "Allan",     Last = "Sandberg",  ID = 912, City = "Seattle" },
        new() { First = "Dmitry",    Last = "Degtyarev", ID = 923, City = "Redmond" },
        new() { First = "Arinze",    Last = "Madu",      ID = 934, City = "Seattle" },
        new() { First = "Andrey",    Last = "Glazkov",   ID = 945, City = "Redmond" },
        new() { First = "Kettil",    Last = "Berggren",  ID = 956, City = "Tacoma" },
        new() { First = "Hanying",   Last = "Feng",      ID = 967, City = "Seattle" },
        new() { First = "Hugo",      Last = "Garcia",    ID = 978, City = "Redmond" },
        new() { First = "Michael",   Last = "Tucker",    ID = 989, City = "Tacoma" },
        new() { First = "Ifeanacho", Last = "Jamuike",   ID = 991, City = "Redmond" },
        new() { First = "Carmen",    Last = "Vella",     ID = 982, City = "Tacoma" },
        new() { First = "Noel",      Last = "Svensson",  ID = 973, City = "Seattle" }
   ];
}

Each Student has a grade level, a primary department, and a series of scores. A Teacher also has a City property that identifies the campus where the teacher holds classes. A Department has a name and a reference to a Teacher who serves as the department head.

You can find the data set in the source repo.

Types of query operators

The standard query operators differ in the timing of their execution, depending on whether they return a singleton value or a sequence of values. Those methods that return a singleton value (such as System.Linq.Enumerable.Average and System.Linq.Enumerable.Sum) execute immediately. Methods that return a sequence defer the query execution and return an enumerable object. You can use the output sequence of one query as the input sequence to another query. You chain query methods together in one query, which enables queries to become arbitrarily complex.

Query operators

In a LINQ query, the first step is to specify the data source. In a LINQ query, the from clause comes first in order to introduce the data source (students) and the range variable (student).

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/IndexExamples.csObtainDataSource

csharp
using System.Xml.Linq;

namespace StandardQueryOperators;

internal class IndexExamples
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Sentences();
        PartsOfAQuery();
        XmlTransformation();
    }

    private static void Sentences()
    {
        // <FirstSentence>
        string sentence = "the quick brown fox jumps over the lazy dog";
        // Split the string into individual words to create a collection.
        string[] words = sentence.Split(' ');

        // Using query expression syntax.
        var query = from word in words
                    group word.ToUpper() by word.Length into gr
                    orderby gr.Key
                    select new { Length = gr.Key, Words = gr };

        // Using method-based query syntax.
        var query2 = words.
            GroupBy(w => w.Length, w => w.ToUpper()).
            Select(g => new { Length = g.Key, Words = g }).
            OrderBy(o => o.Length);

        foreach (var obj in query)
        {
            Console.WriteLine($"Words of length {obj.Length}:");
            foreach (string word in obj.Words)
                Console.WriteLine(word);
        }

        // This code example produces the following output:
        //
        // Words of length 3:
        // THE
        // FOX
        // THE
        // DOG
        // Words of length 4:
        // OVER
        // LAZY
        // Words of length 5:
        // QUICK
        // BROWN
        // JUMPS
        // </FirstSentence>
    }

    private static void PartsOfAQuery()
    {
        //<ObtainDataSource>
        //queryAllStudents is an IEnumerable<Student>
        var queryAllStudents = from student in students
                                select student;
        //</ObtainDataSource>
    }

    private static void XmlTransformation()
    {
        // <XmlTransformation>

        // Create the query.
        var studentsToXML = new XElement("Root",
            from student in students
            let scores = string.Join(",", student.Scores)
            select new XElement("student",
                        new XElement("First", student.FirstName),
                        new XElement("Last", student.LastName),
                        new XElement("Scores", scores)
                    ) // end "student"
                ); // end "Root"

        // Execute the query.
        Console.WriteLine(studentsToXML);
        // </XmlTransformation>
        /* Output:
          // <XmlTransformationOutput>
          <Root>
            <student>
              <First>Svetlana</First>
              <Last>Omelchenko</Last>
              <Scores>97,90,73,54</Scores>
            </student>
            <student>
              <First>Claire</First>
              <Last>O'Donnell</Last>
              <Scores>56,78,95,95</Scores>
            </student>
            ...
            <student>
              <First>Max</First>
              <Last>Lindgren</Last>
              <Scores>86,88,96,63</Scores>
            </student>
            <student>
              <First>Arina</First>
              <Last>Ivanova</Last>
              <Scores>93,63,70,80</Scores>
            </student>
          </Root>
          // </XmlTransformationOutput>
        */
    }
}

The range variable is like the iteration variable in a foreach loop except that no actual iteration occurs in a query expression. When the query is executed, the range variable serves as a reference to each successive element in students. Because the compiler can infer the type of student, you don't have to specify it explicitly. You can introduce more range variables in a let clause. For more information, see let clause.

Once you obtain a data source, you can perform any number of operations on that data source:

Query expression syntax table

The following table lists the standard query operators that have equivalent query expression clauses.

Method C# query expression syntax
System.Linq.Enumerable.Cast Use an explicitly typed range variable:

from int i in numbers

(For more information, see from clause.)
System.Linq.Enumerable.GroupBy group … by …

-or-

group … by … into …

(For more information, see group clause.)
System.Linq.Enumerable.GroupJoin4%28System.Collections.Generic.IEnumerable%7B0%7D%2CSystem.Collections.Generic.IEnumerable%7B1%7D%2CSystem.Func%7B0%2C2%7D%2CSystem.Func%7B1%2C2%7D%2CSystem.Func%7B0%2CSystem.Collections.Generic.IEnumerable%7B1%7D%2C3%7D%29 join … in … on … equals … into …

(For more information, see join clause.)
System.Linq.Enumerable.Join4%28System.Collections.Generic.IEnumerable%7B0%7D%2CSystem.Collections.Generic.IEnumerable%7B1%7D%2CSystem.Func%7B0%2C2%7D%2CSystem.Func%7B1%2C2%7D%2CSystem.Func%7B0%2C1%2C3%7D%29 join … in … on … equals …

(For more information, see join clause.)
System.Linq.Enumerable.OrderBy%28System.Collections.Generic.IEnumerable%7B%7D%2CSystem.Func%7B%2C%7D%29 orderby

(For more information, see orderby clause.)
System.Linq.Enumerable.OrderByDescending%28System.Collections.Generic.IEnumerable%7B%7D%2CSystem.Func%7B%2C%7D%29 orderby … descending

(For more information, see orderby clause.)
System.Linq.Enumerable.Select select

(For more information, see select clause.)
System.Linq.Enumerable.SelectMany Multiple from clauses.

(For more information, see from clause.)
System.Linq.Enumerable.ThenBy%28System.Linq.IOrderedEnumerable%7B%7D%2CSystem.Func%7B%2C%7D%29 orderby …, …

(For more information, see orderby clause.)
System.Linq.Enumerable.ThenByDescending%28System.Linq.IOrderedEnumerable%7B%7D%2CSystem.Func%7B%2C%7D%29 orderby …, … descending

(For more information, see orderby clause.)
System.Linq.Enumerable.Where where

(For more information, see where clause.)

Data transformations with LINQ

Language-Integrated Query (LINQ) isn't only about retrieving data. It's also a powerful tool for transforming data. By using a LINQ query, you can use a source sequence as input and modify it in many ways to create a new output sequence. You can modify the sequence itself without modifying the elements themselves by sorting and grouping. But perhaps the most powerful feature of LINQ queries is the ability to create new types. The select clause creates an output element from an input element. You use it to transform an input element into an output element:

  • Merge multiple input sequences into a single output sequence that has a new type.
  • Create output sequences whose elements consist of only one or several properties of each element in the source sequence.
  • Create output sequences whose elements consist of the results of operations performed on the source data.
  • Create output sequences in a different format. For example, you can transform data from SQL rows or text files into XML.

You combine these transformations in various ways in the same query. Furthermore, you can use the output sequence of one query as the input sequence for a new query. The following example transforms objects in an in-memory data structure into XML elements.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/IndexExamples.csXmlTransformation

csharp
using System.Xml.Linq;

namespace StandardQueryOperators;

internal class IndexExamples
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Sentences();
        PartsOfAQuery();
        XmlTransformation();
    }

    private static void Sentences()
    {
        // <FirstSentence>
        string sentence = "the quick brown fox jumps over the lazy dog";
        // Split the string into individual words to create a collection.
        string[] words = sentence.Split(' ');

        // Using query expression syntax.
        var query = from word in words
                    group word.ToUpper() by word.Length into gr
                    orderby gr.Key
                    select new { Length = gr.Key, Words = gr };

        // Using method-based query syntax.
        var query2 = words.
            GroupBy(w => w.Length, w => w.ToUpper()).
            Select(g => new { Length = g.Key, Words = g }).
            OrderBy(o => o.Length);

        foreach (var obj in query)
        {
            Console.WriteLine($"Words of length {obj.Length}:");
            foreach (string word in obj.Words)
                Console.WriteLine(word);
        }

        // This code example produces the following output:
        //
        // Words of length 3:
        // THE
        // FOX
        // THE
        // DOG
        // Words of length 4:
        // OVER
        // LAZY
        // Words of length 5:
        // QUICK
        // BROWN
        // JUMPS
        // </FirstSentence>
    }

    private static void PartsOfAQuery()
    {
        //<ObtainDataSource>
        //queryAllStudents is an IEnumerable<Student>
        var queryAllStudents = from student in students
                                select student;
        //</ObtainDataSource>
    }

    private static void XmlTransformation()
    {
        // <XmlTransformation>

        // Create the query.
        var studentsToXML = new XElement("Root",
            from student in students
            let scores = string.Join(",", student.Scores)
            select new XElement("student",
                        new XElement("First", student.FirstName),
                        new XElement("Last", student.LastName),
                        new XElement("Scores", scores)
                    ) // end "student"
                ); // end "Root"

        // Execute the query.
        Console.WriteLine(studentsToXML);
        // </XmlTransformation>
        /* Output:
          // <XmlTransformationOutput>
          <Root>
            <student>
              <First>Svetlana</First>
              <Last>Omelchenko</Last>
              <Scores>97,90,73,54</Scores>
            </student>
            <student>
              <First>Claire</First>
              <Last>O'Donnell</Last>
              <Scores>56,78,95,95</Scores>
            </student>
            ...
            <student>
              <First>Max</First>
              <Last>Lindgren</Last>
              <Scores>86,88,96,63</Scores>
            </student>
            <student>
              <First>Arina</First>
              <Last>Ivanova</Last>
              <Scores>93,63,70,80</Scores>
            </student>
          </Root>
          // </XmlTransformationOutput>
        */
    }
}

The code produces the following XML output:

sample · xmldocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/IndexExamples.csXmlTransformationOutput

xml
using System.Xml.Linq;

namespace StandardQueryOperators;

internal class IndexExamples
{
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Sentences();
        PartsOfAQuery();
        XmlTransformation();
    }

    private static void Sentences()
    {
        // <FirstSentence>
        string sentence = "the quick brown fox jumps over the lazy dog";
        // Split the string into individual words to create a collection.
        string[] words = sentence.Split(' ');

        // Using query expression syntax.
        var query = from word in words
                    group word.ToUpper() by word.Length into gr
                    orderby gr.Key
                    select new { Length = gr.Key, Words = gr };

        // Using method-based query syntax.
        var query2 = words.
            GroupBy(w => w.Length, w => w.ToUpper()).
            Select(g => new { Length = g.Key, Words = g }).
            OrderBy(o => o.Length);

        foreach (var obj in query)
        {
            Console.WriteLine($"Words of length {obj.Length}:");
            foreach (string word in obj.Words)
                Console.WriteLine(word);
        }

        // This code example produces the following output:
        //
        // Words of length 3:
        // THE
        // FOX
        // THE
        // DOG
        // Words of length 4:
        // OVER
        // LAZY
        // Words of length 5:
        // QUICK
        // BROWN
        // JUMPS
        // </FirstSentence>
    }

    private static void PartsOfAQuery()
    {
        //<ObtainDataSource>
        //queryAllStudents is an IEnumerable<Student>
        var queryAllStudents = from student in students
                                select student;
        //</ObtainDataSource>
    }

    private static void XmlTransformation()
    {
        // <XmlTransformation>

        // Create the query.
        var studentsToXML = new XElement("Root",
            from student in students
            let scores = string.Join(",", student.Scores)
            select new XElement("student",
                        new XElement("First", student.FirstName),
                        new XElement("Last", student.LastName),
                        new XElement("Scores", scores)
                    ) // end "student"
                ); // end "Root"

        // Execute the query.
        Console.WriteLine(studentsToXML);
        // </XmlTransformation>
        /* Output:
          // <XmlTransformationOutput>
          <Root>
            <student>
              <First>Svetlana</First>
              <Last>Omelchenko</Last>
              <Scores>97,90,73,54</Scores>
            </student>
            <student>
              <First>Claire</First>
              <Last>O'Donnell</Last>
              <Scores>56,78,95,95</Scores>
            </student>
            ...
            <student>
              <First>Max</First>
              <Last>Lindgren</Last>
              <Scores>86,88,96,63</Scores>
            </student>
            <student>
              <First>Arina</First>
              <Last>Ivanova</Last>
              <Scores>93,63,70,80</Scores>
            </student>
          </Root>
          // </XmlTransformationOutput>
        */
    }
}

For more information, see Creating XML Trees in C# (LINQ to XML).

You can use the results of one query as the data source for a subsequent query. This example shows how to order the results of a join operation. This query creates a group join and then sorts the groups based on the department element, which is still in scope. Inside the anonymous type initializer, a subquery orders all the matching elements from the students sequence.

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/OrderResultsOfJoin.csOrderResultsOfJoinQuery

csharp
namespace StandardQueryOperators;

public class OrderResultsOfJoin
{
    private static readonly IEnumerable<Department> departments = Sources.Departments;
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Order Results of Join");
        OrderResultsOfJoinQuerySyntax();
        Console.WriteLine("Order Results of Join Method");
        OrderResultsOfJoinMethodSyntax();
    }

    private static void OrderResultsOfJoinQuerySyntax()
    {
        // <OrderResultsOfJoinQuery>
        var orderedQuery = from department in departments
                           join student in students on department.ID equals student.DepartmentID into studentGroup
                           orderby department.Name
                           select new
                           {
                               DepartmentName = department.Name,
                               Students = from student in studentGroup
                                          orderby student.LastName
                                            select student
                           };

        foreach (var departmentList in orderedQuery)
        {
            Console.WriteLine(departmentList.DepartmentName);
            foreach (var student in departmentList.Students)
            {
                Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
            }
        }
        /* Output:
        Chemistry
          Balzan     Josephine
          Fakhouri   Fadi
          Popov      Innocenty
          Seleznyova Sofiya
          Vella      Carmen
        Economics
          Adams      Terry
          Adaobi     Izuchukwu
          Berggren   Jeanette
          Garcia     Cesar
          Ifeoma     Nwanneka
          Jamuike    Ifeanacho
          Larsson    Naima
          Svensson   Noel
          Ugomma     Ifunanya
        Engineering
          Axelsson   Erik
          Berg       Veronika
          Engström   Nancy
          Hicks      Cassie
          Keever     Bruce
          Micallef   Nicholas
          Mortensen  Sven
          Nilsson    Erna
          Tucker     Michael
          Yermolayeva Anna
        English
          Andersson  Sarah
          Feng       Hanying
          Ivanova    Arina
          Jakobsson  Jesper
          Jensen     Christiane
          Johansson  Mark
          Kolpakova  Nadezhda
          Omelchenko Svetlana
          Urquhart   Donald
        Mathematics
          Frost      Gaby
          Garcia     Hugo
          Hedlund    Anna
          Kovaleva   Katerina
          Lindgren   Max
          Maslova    Evgeniya
          Olsson     Ruth
          Sammut     Maria
          Sazonova   Anastasiya
        Physics
          Åkesson    Sami
          Edwards    Amy E.
          Falzon     John
          Garcia     Debra
          Hansson    Sanna
          Mattsson   Martina
          Richardson Don
          Zabokritski Eugene
        */
        // </OrderResultsOfJoinQuery>
    }

    private static void OrderResultsOfJoinMethodSyntax()
    {
        // <OrderResultsOfJoinMethod>
        var orderedQuery = departments
            .GroupJoin(students, department => department.ID, student => student.DepartmentID,
            (department, studentGroup) => new
            {
                DepartmentName = department.Name,
                Students = studentGroup.OrderBy(student => student.LastName)
            })
            .OrderBy(department => department.DepartmentName);


        foreach (var departmentList in orderedQuery)
        {
            Console.WriteLine(departmentList.DepartmentName);
            foreach (var student in departmentList.Students)
            {
                Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
            }
        }
        // </OrderResultsOfJoinMethod>
    }
}

The equivalent query using method syntax is shown in the following code:

sample · csharpdocs/csharp/linq/standard-query-operators/snippets/standard-query-operators/OrderResultsOfJoin.csOrderResultsOfJoinMethod

csharp
namespace StandardQueryOperators;

public class OrderResultsOfJoin
{
    private static readonly IEnumerable<Department> departments = Sources.Departments;
    private static readonly IEnumerable<Student> students = Sources.Students;

    public static void RunAllSnippets()
    {
        Console.WriteLine("Order Results of Join");
        OrderResultsOfJoinQuerySyntax();
        Console.WriteLine("Order Results of Join Method");
        OrderResultsOfJoinMethodSyntax();
    }

    private static void OrderResultsOfJoinQuerySyntax()
    {
        // <OrderResultsOfJoinQuery>
        var orderedQuery = from department in departments
                           join student in students on department.ID equals student.DepartmentID into studentGroup
                           orderby department.Name
                           select new
                           {
                               DepartmentName = department.Name,
                               Students = from student in studentGroup
                                          orderby student.LastName
                                            select student
                           };

        foreach (var departmentList in orderedQuery)
        {
            Console.WriteLine(departmentList.DepartmentName);
            foreach (var student in departmentList.Students)
            {
                Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
            }
        }
        /* Output:
        Chemistry
          Balzan     Josephine
          Fakhouri   Fadi
          Popov      Innocenty
          Seleznyova Sofiya
          Vella      Carmen
        Economics
          Adams      Terry
          Adaobi     Izuchukwu
          Berggren   Jeanette
          Garcia     Cesar
          Ifeoma     Nwanneka
          Jamuike    Ifeanacho
          Larsson    Naima
          Svensson   Noel
          Ugomma     Ifunanya
        Engineering
          Axelsson   Erik
          Berg       Veronika
          Engström   Nancy
          Hicks      Cassie
          Keever     Bruce
          Micallef   Nicholas
          Mortensen  Sven
          Nilsson    Erna
          Tucker     Michael
          Yermolayeva Anna
        English
          Andersson  Sarah
          Feng       Hanying
          Ivanova    Arina
          Jakobsson  Jesper
          Jensen     Christiane
          Johansson  Mark
          Kolpakova  Nadezhda
          Omelchenko Svetlana
          Urquhart   Donald
        Mathematics
          Frost      Gaby
          Garcia     Hugo
          Hedlund    Anna
          Kovaleva   Katerina
          Lindgren   Max
          Maslova    Evgeniya
          Olsson     Ruth
          Sammut     Maria
          Sazonova   Anastasiya
        Physics
          Åkesson    Sami
          Edwards    Amy E.
          Falzon     John
          Garcia     Debra
          Hansson    Sanna
          Mattsson   Martina
          Richardson Don
          Zabokritski Eugene
        */
        // </OrderResultsOfJoinQuery>
    }

    private static void OrderResultsOfJoinMethodSyntax()
    {
        // <OrderResultsOfJoinMethod>
        var orderedQuery = departments
            .GroupJoin(students, department => department.ID, student => student.DepartmentID,
            (department, studentGroup) => new
            {
                DepartmentName = department.Name,
                Students = studentGroup.OrderBy(student => student.LastName)
            })
            .OrderBy(department => department.DepartmentName);


        foreach (var departmentList in orderedQuery)
        {
            Console.WriteLine(departmentList.DepartmentName);
            foreach (var student in departmentList.Students)
            {
                Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
            }
        }
        // </OrderResultsOfJoinMethod>
    }
}

Although you can use an orderby clause with one or more of the source sequences before the join, we generally don't recommend it. Some LINQ providers might not preserve that ordering after the join. For more information, see join clause.

See also