Toán tử tổng hợp trong LINQ

Các toán tử tổng hợp thực hiện các phép toán như Average, Aggregate, Count, Max, Min và Sum trên các thuộc tính của các phần tử trong danh sách.

Bảng sau liệt kê các toán tử tổng hợp có sẵn trong LINQ:

Phương thức Mô tả
Aggregate Thực hiện một thao tác tổng hợp tùy chỉnh trên các giá trị trong danh sách.
Average Tính trung bình của các phần tử kiểu số trong danh sách.
Count Đếm các yếu tố trong danh sách và trả về kiểu int.
LongCount Đếm các phần tử trong danh sách và trả về kiểu long.
Max Tìm giá trị lớn nhất trong danh sách.
Min Tìm giá trị nhỏ nhất trong danh sách.
Sum Tính tổng các giá trị trong danh sách.

Toán tử tổng hợp là một trong những toán tử truy vấn chuẩn của LINQ.

Toán tử truy vấn chuẩn của LINQ | Comdy
Các toán tử truy vấn chuẩn trong LINQ là các phương thức mở rộng cho các kiểu IEnumerable<T> và IQueryable<T>.

Toán tử Aggregate trong LINQ

Phương thức Aggregate trong Cú pháp phương thức LINQ

Phương thức Aggregate thực hiện một thao tác tổng hợp tùy chỉnh trên các giá trị trong danh sách. Phương thức mở rộng Aggregate có các phương thức quá tải sau:


public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, 
    Func<TSource, TSource, TSource> func);

public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, 
    TAccumulate seed, 
    Func<TAccumulate, TSource, TAccumulate> func);

public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, 
    TAccumulate seed, 
    Func<TAccumulate, TSource, TAccumulate> func, 
    Func<TAccumulate, TResult> resultSelector);

Ví dụ sau đây cho thấy phương thức Aggregate sẽ tổng hợp các phần tử vào trong một chuỗi được phân tách bằng dấu phẩy từ một danh sách kiểu chuỗi.


IList<String> strList = new List<String>() { "One", "Two", "Three", "Four", "Five"};

var commaSeperatedString = strList.Aggregate((s1, s2) => s1 + ", " + s2);

Console.WriteLine(commaSeperatedString);

Đây là kết quả khi biên dịch và thực thi chương trình:

One, Two, Three, Four, Five

Trong ví dụ trên, phương thức mở rộng Aggregate trả về các chuỗi gồm các phần tử được phân tách bằng dấu phẩy từ danh sách strList. Hình ảnh sau đây minh họa toàn bộ thao tác tổng hợp được thực hiện trong ví dụ trên.

Theo hình trên, phần tử đầu tiên của strList là "One" sẽ được truyền dưới dạng s1 và phần còn lại của các mục sẽ được chuyển thành s2.

Biểu thức lambda (s1, s2) => s1 + ", " + s2 sẽ được xử lý như s1 = s1 + ", " + s2 nơi s1 sẽ được tích lũy cho từng phần tử trong danh sách.

Do đó, phương thức Aggregate sẽ trả về chuỗi các phần tử được phân tách bằng dấu phẩy.

Phương thức Aggregate với giá trị Seed

Phương thức quá tải thứ hai của Aggregate yêu cầu tham số đầu tiên cho giá trị seed để tích lũy.

Tham số thứ hai là delegate kiểu Func như sau:
TAccumulate Aggregate<TSource, TAccumulate>(TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);

Ví dụ sau sử dụng chuỗi làm giá trị seed trong phương thức mở rộng Aggregate.

// Student collection
IList<Student> studentList = new List<Student>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13} ,
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 } ,
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 } ,
    new Student() { StudentID = 4, StudentName = "Ram" , Age = 20} ,
    new Student() { StudentID = 5, StudentName = "Ron" , Age = 15 } 
};

string result = studentList.Aggregate<Student, string>(
    "Student Names: ",  // seed value
    (str, s) => str += s.StudentName + "," ); 

Console.WriteLine(result);

Đây là kết quả khi biên dịch và thực thi chương trình:

Student Names: John,Moin,Bill,Ram,Ron,

Trong ví dụ trên, tham số đầu tiên của phương thức Aggregate là chuỗi "Student Names:" sẽ được tích lũy với thuộc tính StudentName của các sinh viên.

Ví dụ sau sử dụng toán tử Aggregate để tính tổng tuổi của tất cả sinh viên.

// Student collection
IList<Student> studentList = new List<Student>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13 },
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
    new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
    new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
};

int sumOfStudentsAge = studentList
    .Aggregate<Student, int>(0, (age, s) => age += s.Age); 

Console.WriteLine(sumOfStudentsAge);

Đây là kết quả khi biên dịch và thực thi chương trình:

87

Phương thức Aggregate với bộ chọn kết quả

Bây giờ, hãy xem phương thức quá tải thứ ba yêu cầu tham số thứ ba của biểu thức delegate Func cho bộ chọn kết quả, để bạn có thể định dạng kết quả  trả về.

// Student collection
IList<Student> studentList = new List<Student>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13 },
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
    new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
    new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
};

string commaSeparatedStudentNames = studentList.Aggregate<Student, string,string>(
    String.Empty, // seed value
    (str, s) => str += s.StudentName + ",", // returns result using seed value, String.Empty goes to lambda expression as str
    str => str.Substring(0,str.Length - 1 )); // result selector that removes last comma

Console.WriteLine(commaSeparatedStudentNames);

Đây là kết quả khi biên dịch và thực thi chương trình:

John,Moin,Bill,Ram,Ron

Trong ví dụ trên, chúng tôi đã chỉ định biểu thức lambda str => str.Substring(0,str.Length - 1 ) sẽ xóa dấu phẩy cuối cùng trong chuỗi kết quả.

Lưu ý: Toán tử Aggregate không hỗ trợ cú pháp truy vấn LINQ.

Toán tử Average trong LINQ

Phương thức mở rộng Average tính giá trị trung bình của các phần tử trong danh sách. Phương thức Average trả về giá trị kiểu decimal, double hoặc float có hỗ trợ kiểu nullable.

Ví dụ sau minh họa phương thức Average trả về giá trị trung bình của tất cả các số nguyên trong danh sách.

IList<int> intList = new List<int>() { 10, 20, 30 };

var avg = intList.Average();

Console.WriteLine("Average: {0}", avg);

Đây là kết quả khi biên dịch và thực thi chương trình:

Average: 20

Bạn có thể chỉ định một thuộc tính kiểu int, decimal, double hoặc float của một lớp dưới dạng biểu thức lambda mà bạn muốn tính giá trị trung bình. Ví dụ sau đây cho thấy phương thức Average trên kiểu phức tạp.

IList<Student> studentList = new List<Student>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13 },
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
    new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
    new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
};

var avgAge = studentList.Average(s => s.Age);

Console.WriteLine("Average Age of Student: {0}", avgAge);

Đây là kết quả khi biên dịch và thực thi chương trình:

Average Age of Student: 17.4
Lưu ý: Toán tử Average không hỗ trợ  cú pháp truy vấn trong LINQ.

Toán tử Count trong LINQ

Toán tử Count trả về số lượng phần tử trong tổng hợp hoặc đếm số phần tử thỏa mãn điều kiện đã cho.

Phương thức mở rộng Count có hai phương thức quá tải sau:

int Count<TSource>();

int Count<TSource>(Func<TSource, bool> predicate);

Phương thức quá tải đầu tiên của Count trả về số lượng phần tử trong tổng hợp đã chỉ định. Phương thức quá tải thứ hai trả về số phần tử thỏa mãn điều kiện đã chỉ định được đưa ra dưới dạng hàm vị ngữ / biểu thức lambda.

Ví dụ sau đây minh họa cách sử dụng phương thức Count trên danh sách kiểu nguyên thủy.

IList<int> intList = new List<int>() { 10, 21, 30, 45, 50 };

var totalElements = intList.Count();

Console.WriteLine("Total Elements: {0}", totalElements);

var evenElements = intList.Count(i => i % 2 == 0);

Console.WriteLine("Even Elements: {0}", evenElements);

Đây là kết quả khi biên dịch và thực thi chương trình:

Total Elements: 5
Even Elements: 3

Ví dụ sau đây min họa phương thức Count trên danh sách có kiểu phức tạp.

IList<Student> studentList = new List<Student>>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13 },
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
    new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
    new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
};

var totalStudents = studentList.Count();

Console.WriteLine("Total Students: {0}", totalStudents);

var adultStudents = studentList.Count(s => s.Age >= 18);

Console.WriteLine("Number of Adult Students: {0}", adultStudents );

Đây là kết quả khi biên dịch và thực thi chương trình:

Total Students: 5
Number of Adult Students: 3
Lưu ý: Toán tử Count không hỗ trợ  cú pháp truy vấn trong LINQ.

Toán tử LongCount trong LINQ

Toán tử LongCount tương tự như toán tử Count - đó là trả về số lượng phần tử trong tổng hợp hoặc đếm số phần tử thỏa mãn điều kiện đã cho.

Tuy nhiên nó khác toán tử Count ở chỗ toán tử Count trả về dữ liệu kiểu int còn LongCount trả về dữ liệu kiểu long (sử dụng cho những danh sách có số phần tử siêu lớn vượt quá giới hạn kích thước kiểu int).

Phương thức mở rộng LongCount có hai phương thức quá tải sau:

long LongCount<TSource>();

long LongCount<TSource>(Func<TSource, bool> predicate);

Ví dụ sau đây minh họa cách sử dụng phương thức LongCount trên danh sách kiểu nguyên thủy.

IList<int> intList = new List<int>() { 10, 21, 30, 45, 50, 60, 70 };

var totalElements = intList.LongCount();

Console.WriteLine("Total Elements: {0}", totalElements);

var evenElements = intList.LongCount(i => i % 2 == 0);

Console.WriteLine("Even Elements: {0}", evenElements);

Đây là kết quả khi biên dịch và thực thi chương trình:

Total Elements: 7
Even Elements: 4

Ví dụ sau đây min họa phương thức LongCount trên danh sách có kiểu phức tạp.

IList<Student> studentList = new List<Student>>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13 },
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
    new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
    new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
};

var totalStudents = studentList.LongCount();

Console.WriteLine("Total Students: {0}", totalStudents);

var teenStudents = studentList.LongCount(s => s.Age > 12 && s.Age < 20);

Console.WriteLine("Number of teen Students: {0}", teenStudents);

Đây là kết quả khi biên dịch và thực thi chương trình:

Total Students: 5
Number of teen Students: 3
Lưu ý: Toán tử LongCount không hỗ trợ  cú pháp truy vấn trong LINQ.

Toán tử Max trong LINQ

Toán tử Max trả về phần tử kiểu số lớn nhất từ ​​danh sách.

Ví dụ sau đây minh họa cách sử dụng phương thức Max trên danh sách kiểu nguyên thủy.

IList<int> intList = new List<int>() { 10, 21, 30, 45, 50, 87 };

var largest = intList.Max();

Console.WriteLine("Largest Element: {0}", largest);

var largestEvenElements = intList.Max(i => i % 2 == 0 ? i : 0);

Console.WriteLine("Largest Even Element: {0}", largestEvenElements );

Đây là kết quả khi biên dịch và thực thi chương trình:

Largest Element: 87
Largest Even Element: 50

Ví dụ sau đây minh họa sử dụng phương thức Max trên danh sách kiểu phức tạp.

IList<Student> studentList = new List<Student>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13 },
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
    new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
    new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
};

var oldest = studentList.Max(s => s.Age);

Console.WriteLine("Oldest Student Age: {0}", oldest);

Đây là kết quả khi biên dịch và thực thi chương trình:

Oldest Student Age: 21

Phương thức Max trả về kết quả của bất kỳ kiểu dữ liệu nào. Ví dụ sau đây cho thấy cách bạn có thể tìm thấy một sinh viên có tên dài nhất trong danh sách:

public class Student : IComparable<Student> 
{
    public int StudentID { get; set; }
    public string StudentName { get; set; }
    public int Age { get; set; }
    public int StandardID { get; set; }

    public int CompareTo(Student other)
    {
        if (this.StudentName.Length > other.StudentName.Length)
        {
            return 1;
        }
        return 0;
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Student collection
        IList<Student> studentList = new List<Student>() 
        { 
            new Student() { StudentID = 1, StudentName = "Johnny", Age = 13 },
            new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
            new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
            new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
        };

        var studentWithLongName = studentList.Max();

        Console.WriteLine("Student ID: {0}, Student Name: {1}", 
            studentWithLongName.StudentID, studentWithLongName.StudentName)
    }
}

Đây là kết quả khi biên dịch và thực thi chương trình:

Student ID: 1, Student Name: Johnny

Theo ví dụ trên, để tìm sinh viên có tên dài nhất, bạn cần triển khai interface IComparable<T> cho lớp Student và so sánh độ dài của tên sinh viên trong phương thức CompareTo.

Vì vậy, bây giờ khi bạn sử dụng phương thức Max, nó sẽ gọi phương thức CompareTo để trả về kết quả phù hợp.

Lưu ý: Toán tử Max không hỗ trợ  cú pháp truy vấn trong LINQ.

Toán tử Min trong LINQ

Trái ngược với toán tử Max, toán tử Min trả về phần tử kiểu số nhỏ nhất từ ​​danh sách.

Ví dụ sau đây minh họa cách sử dụng phương thức Min trên danh sách kiểu nguyên thủy.

IList<int> intList = new List<int>() { 10, 21, 30, 45, 50, 87 };

var smallest = intList.Min();

Console.WriteLine("Smallest Element: {0}", smallest);

var smallestOddElements = intList.Min(i => i % 2 != 0 ? i : 0);

Console.WriteLine("Smallest Odd Element: {0}", smallestOddElements );

Đây là kết quả khi biên dịch và thực thi chương trình:

Smallest Element: 10
Smallest Odd Element: 21

Ví dụ sau đây minh họa sử dụng phương thức Min trên danh sách kiểu phức tạp.

IList<Student> studentList = new List<Student>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13 },
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
    new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
    new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
};

var youngest = studentList.Min(s => s.Age);

Console.WriteLine("Youngest Student Age: {0}", oldest);

Đây là kết quả khi biên dịch và thực thi chương trình:

Youngest Student Age: 13

Phương thức Min trả về kết quả của bất kỳ kiểu dữ liệu nào. Ví dụ sau đây cho thấy cách bạn có thể tìm thấy một sinh viên có tên ngắn nhất trong danh sách:

public class Student : IComparable<Student> 
{
    public int StudentID { get; set; }
    public string StudentName { get; set; }
    public int Age { get; set; }
    public int StandardID { get; set; }

    public int CompareTo(Student other)
    {
        if (this.StudentName.Length > other.StudentName.Length)
        {
            return 1;
        }
        else if (this.StudentName.Length < other.StudentName.Length)
        {
            return -1;
        }
        return 0;
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Student collection
        IList<Student> studentList = new List<Student>() 
        { 
            new Student() { StudentID = 1, StudentName = "John", Age = 13 },
            new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
            new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
            new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
            new Student() { StudentID = 5, StudentName = "Ronney", Age = 15 } 
        };

        var studentWithShortName = studentList.Min();

        Console.WriteLine("Student ID: {0}, Student Name: {1}", 
            studentWithShortName.StudentID, studentWithShortName.StudentName)
    }
}

Đây là kết quả khi biên dịch và thực thi chương trình:

Student ID: 4, Student Name: Ram
Lưu ý: Toán tử Min không hỗ trợ  cú pháp truy vấn trong LINQ.

Toán tử Sum trong LINQ

Toán tử Sum tính tổng các phần tử kiểu số trong danh sách.

Ví dụ sau đây minh họa sử dụng phương thức Sum trên danh sách kiểu nguyên thủy.

IList<int> intList = new List<int>() { 10, 21, 30, 45, 50, 87 };

var total = intList.Sum();

Console.WriteLine("Sum: {0}", total);

var sumOfEvenElements = intList.Sum(i => i % 2 == 0 ? i : 0);

Console.WriteLine("Sum of Even Elements: {0}", sumOfEvenElements );

Đây là kết quả khi biên dịch và thực thi chương trình:

Sum: 243
Sum of Even Elements: 90

Ví dụ sau đây tính tổng số tuổi của tất cả học sinh và số lượng học sinh trưởng thành trong danh sách học sinh.

IList<Student> studentList = new List<Student>>() 
{ 
    new Student() { StudentID = 1, StudentName = "John", Age = 13 },
    new Student() { StudentID = 2, StudentName = "Moin",  Age = 21 },
    new Student() { StudentID = 3, StudentName = "Bill",  Age = 18 },
    new Student() { StudentID = 4, StudentName = "Ram", Age = 20 },
    new Student() { StudentID = 5, StudentName = "Ron", Age = 15 } 
};

var sumOfAge = studentList.Sum(s => s.Age);

Console.WriteLine("Sum of all student's age: {0}", sumOfAge);
		
var numOfAdults = studentList.Sum(s => s.Age >= 18 ? 1 : 0);
 
Console.WriteLine("Total Adult Students: {0}", numOfAdults);

Đây là kết quả khi biên dịch và thực thi chương trình:

Total Age of Student: 87
Total Adult Students: 3
Lưu ý: Toán tử Sum không hỗ trợ  cú pháp truy vấn trong LINQ.


Bài viết liên quan:

Bạn sẽ tìm hiểu một số truy vấn LINQ phức tạp trong hướng dẫn này.

Từ khóa let, into trong LINQ có tác dụng gì? Hướng dẫn khai báo và sử dụng từ khóa let, into trong LINQ.

Trì hoãn thực thi truy vấn LINQ là gì? Thực thi ngay lập tức truy vấn LINQ là gì? Làm sao để thực thi truy vấn LINQ.