TOC

This article is currently in the process of being translated into Vietnamese (~99% done).

LINQ:

Grouping data: the GroupBy() Method

Chúng ta đã làm việc với danh sách dữ liệu. Chúng ta đã sắp xếp, giới hạn và định dạng chúng vào các đối tượng mới, nhưng một thao tác quan trọng vẫn còn thiếu là nhóm dữ liệu. Khi bạn nhóm dữ liệu, bạn lấy một danh sách và chia ra thành nhiều nhóm nhỏ dựa trên một hay nhiều thuộc tính. Thử tưởng tượng rằng chúng ta có dữ liệu gốc như sau:

var users = new List<User>()
{
    new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
    new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
    new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
    new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
    new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
};

Một danh sách đối tượng người dùng nhưng nó có thể thú vị nếu nhóm những người dùng này lại, ví dụ theo quốc gia hay tuổi. Với LINQ, điều này rất dễ, kể cả dùng GroupBy() có thể hơi lạ lúc đầu. Hãy nhìn cách hoạt động của nó:

using System;    
using System.Collections.Generic;    
using System.Linq;    

namespace LinqGroup    
{    
    class Program    
    {    
static void Main(string[] args)    
{    
    var users = new List<User>()    
    {    
new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },    
new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },    
new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },    
new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },    
new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },    
    };    
    var usersGroupedByCountry = users.GroupBy(user => user.HomeCountry);    
    foreach(var group in usersGroupedByCountry)    
    {    
Console.WriteLine("Users from " + group.Key + ":");    
foreach(var user in group)    
Console.WriteLine("* " + user.Name);
    }    
}    

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

    public int Age { get; set; }    

    public string HomeCountry { get; set; }    
}    
    }    
}

Kết quả đầu ra sẽ giống như sau:

Users from USA:
* John Doe
* Jane Doe
* James Doe
Users from Germany:
* Joe Doe
* Jenna Doe

Ví dụ hơi dài nhưng như bạn thấy, phần lớn là chuẩn bị dữ liệu. Nhớ rằng toàn bộ dữ liệu tới từ XML hay cơ sở dữ liệu - chỉ là dễ để minh họa để sử dụng.

Phần thú vị ở đây là khi chúng ta tạo ra biến usersGroupedByCountry. Chúng ta gọi hàm GroupBy(), truyền tham số chúng ta muốn nhóm vào. Trong trường hợp này là quốc gia. Kết quả là một đối tượng với thuộc tính Key, giữ giá trị thuộc tính chúng ta nhóm theo (HomeCountry) cũng như là toàn bộ đối tượng thuộc về nhóm. Chúng ta dùng nó ở dòng tiếp theo để lặp lại trong nhóm và in ra Key cùng với các đối tượng cần thiết.

Khóa nhóm tùy chỉnh

Như bạn có thể thấy, việc nhóm theo một thuộc tính hiện có rất dễ dàng, nhưng như bạn có thể đã biết, các phương pháp LINQ rất linh hoạt. Thật đơn giản để tạo các nhóm tùy chỉnh của riêng bạn, dựa trên bất kỳ thứ gì bạn thích - ví dụ về điều đó có thể là như sau, nơi chúng tôi tạo các nhóm dựa trên hai chữ cái đầu tiên của tên người dùng:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqGroup
{
    class Program
    {
static void Main(string[] args)
{
    var users = new List<User>()
    {
new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
    };
    var usersGroupedByFirstLetters = users.GroupBy(user => user.Name.Substring(0, 2));
    foreach(var group in usersGroupedByFirstLetters)
    {
Console.WriteLine("Users starting with " + group.Key + ":");
foreach(var user in group)
    Console.WriteLine("* " + user.Name);
    }
}

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

    public int Age { get; set; }

    public string HomeCountry { get; set; }
}
    }
}

Chúng ta chỉ gọi phương thức Substring() cho tên để lấy hai ký tự đầu tiên và sau đó là LINQ tạo ra các nhóm dựa trên đó. Kết quả như sau:

Users starting with Jo:
* John Doe
* Joe Doe
Users starting with Ja:
* Jane Doe
* James Doe
Users starting with Je:
* Jenna Doe

Vì bậy bạn có thể thấy, chúng ta thoải mái gọi phương thức trong GroupBy() - thực tế, chúng ta có thể làm được bất kỳ những gì chúng ta muốn ngay khi chúng ta trả về kết quả thì LINQ có thể nhóm các kết quả. Chúng ta thậm chí có thể tạo ra phương thức trả về thông tin mới và sau đó dùng chúng để tạo nhóm như trong ví dụ:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqGroup
{
    class Program
    {
static void Main(string[] args)
{
    var users = new List<User>()
    {
new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
    };
    var usersGroupedByAgeGroup = users.GroupBy(user => user.GetAgeGroup());
    foreach(var group in usersGroupedByAgeGroup)
    {
Console.WriteLine(group.Key + ":");
foreach(var user in group)
    Console.WriteLine("* " + user.Name + " [" + user.Age + " years]");
    }
}

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

    public int Age { get; set; }

    public string HomeCountry { get; set; }

    public string GetAgeGroup()
    {
if (this.Age < 13)
    return "Children";
if (this.Age < 20)
    return "Teenagers";
return "Adults";
    }
}
    }
}

Chú ý cách tôi thực hiện GetAgeGroup() trong lớp User. Nó trả về chuỗi định nghĩa tuổi của user và chúng ta chỉ gọi trong GroupBy() để dùng như là key của nhóm. Kết quả như sau:

Adults:
* John Doe [42 years]
* Jane Doe [38 years]
Teenagers:
* Joe Doe [19 years]
* Jenna Doe [19 years]
Children:
* James Doe [8 years]

Tôi chọn thực hiện GetAgeGroup() trong lớp User vì nó có thể hữu ích nhưng đôi khi bạn chỉ cần nhóm group nhanh chóng và không cần dùng lại; Khi đó bạn có thể thực hiện ngay trong GroupBy() như lambda:

var usersGroupedByAgeGroup = users.GroupBy(user =>
    {
if (user.Age < 13)
    return "Children";
if (user.Age < 20)
    return "Teenagers";
return "Adults";
    });

Kết quả tất nhiên y hệt!

Nhóm theo một khóa tổng hợp

Khóa của nhóm là giá trị đơn trong ví dụ. Tuy nhiên, bạn có thể tạo ra khóa nhiều giá trị - gọi là khóa kết hợp. Ví dụ nếu chúng ta tạo ra nhóm user dựa trên quốc gia và tuổi như sau:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqGroup2
{
    class Program
    {
static void Main(string[] args)
{
    var users = new List<User>()
    {
new User { Name = "John Doe", Age = 42, HomeCountry = "USA" },
new User { Name = "Jane Doe", Age = 38, HomeCountry = "USA" },
new User { Name = "Joe Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "Jenna Doe", Age = 19, HomeCountry = "Germany" },
new User { Name = "James Doe", Age = 8, HomeCountry = "USA" },
    };

    var usersGroupedByCountryAndAge = users.GroupBy(user => new { user.HomeCountry, user.Age });
    foreach(var group in usersGroupedByCountryAndAge)
    {
Console.WriteLine("Users from " + group.Key.HomeCountry + " at the age of " + group.Key.Age + ":");
foreach (var user in group)
    Console.WriteLine("* " + user.Name + " [" + user.Age + " years]");
    }
}

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

    public int Age { get; set; }

    public string HomeCountry { get; set; }

}
    }
}

Chú ý cú pháp dùng trong GroupBy() - thay vì đưa vào một thuộc tính đơn thì chúng ta dùng đối tượng nặc danh chứa HomeCountry và Age. LINQ sẽ tạo ra group dựa vào hai thuộc tính này và dùng đối tượng nặc danh làm Key của group. Chúng ta dùng cả hai thuộc tính khi lặp qua nhóm như bạn thấy. Kết quả như sau:

Users from USA at the age of 42:
* John Doe [42 years]
Users from USA at the age of 38:
* Jane Doe [38 years]
Users from Germany at the age of 19:
* Joe Doe [19 years]
* Jenna Doe [19 years]
Users from USA at the age of 8:
* James Doe [8 years]

Chúng ta dùng phương thức LINQ nhưng tôi có thể minh họa cho bạn ví dụ về cú pháp truy vấn LINQ:

// Method syntax
var usersGroupedByCountryAndAge = users.GroupBy(user => new { user.HomeCountry, user.Age });
// Query syntax
var usersGroupedByCountryAndAgeQ = from user in users group user by new { user.HomeCountry, user.Age } into userGroup select userGroup;

Tóm lại

Như bạn thấy trong ví dụ thì GroupBy() của LINQ rqqts hữu ích. Nó cho phép bạn dùng dữ liệu khác đi với ít dòng code. Trước đây có thể dài dòng và cần cơ sở dữ liệu quan hệ nhưng với LINQ bạn có thể dùng bất kỳ nguồn dữ liệu nào bạn muốn và có kết quả tương tự dễ dùng.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!