2009-03-30 32 views
13

Tôi có một vấn đề sử dụng LINQ để đặt hàng một cấu trúc như thế này:Sắp xếp một truy vấn Danh sách <T> sử dụng biểu thức

public class Person 
{ 
    public int ID { get; set; } 
    public List<PersonAttribute> Attributes { get; set; } 
} 

public class PersonAttribute 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 
    public string Value { get; set; } 
} 

Một người có thể đi như thế này:

PersonAttribute Age = new PersonAttribute { ID = 8, Name = "Age", Value = "32" }; 
PersonAttribute FirstName = new PersonAttribute { ID = 9, Name = "FirstName", Value = "Rebecca" }; 
PersonAttribute LastName = new PersonAttribute { ID = 10, Name = "LastName", Value = "Johnson" }; 
PersonAttribute Gender = new PersonAttribute { ID = 11, Name = "Gender", Value = "Female" }; 

Tôi muốn sử dụng LINQ phép chiếu để sắp xếp danh sách những người tăng dần theo thuộc tính người mà tôi chọn, ví dụ, sắp xếp theo Độ tuổi hoặc sắp xếp trên FirstName.

tôi đang cố gắng một cái gì đó giống như

string mySortAttribute = "Age" 
PersonList.OrderBy(p => p.PersonAttribute.Find(s => s.Name == mySortAttribute).Value); 

Nhưng cú pháp là không cho tôi. Bất kì manh mối nào?

Trả lời

5

Tại sao bạn không sử dụng từ điển có giá trị khóa thay vì Danh sách <PersonAttribute> của bạn? Nó sẽ phù hợp hơn, tôi nghĩ, và làm cho mọi thứ khác dễ dàng hơn.

Update - như thế này:

public class Person 
{ 
    public Dictionary<string, string> Attributes = new Dictionary<string,string>(); 
} 

List<Person> people = new List<Person>(); 

Person rebecca = new Person(); 
rebecca.Attributes["Age"] = "32"; 
rebecca.Attributes["FirstName"] = "Rebecca"; 
rebecca.Attributes["LastName"] = "Johnson"; 
rebecca.Attributes["Gender"] = "Female"; 
people.Add(rebecca); 

var PeopleInAgeOrder = people.OrderBy(p => p.Attributes["Age"]); 
+10

này không trả lời câu hỏi của anh. Ông không hỏi làm thế nào để cơ cấu lại dữ liệu của mình, ông hỏi làm thế nào để sắp xếp một cấu trúc dữ liệu (có lẽ là tồn tại, và có lẽ không thể thay đổi). –

0

Nó có thể được rằng cú pháp của bạn là sai? Tài sản của bạn được gọi là thuộc tính nhưng bạn sử dụng một cái gì đó gọi là ObjectSettings trong mã? Hoặc là một lỗi đánh máy.

Nếu có thì mã của bạn sẽ ổn, trừ khi không phải tất cả các cá thể của Cá nhân đều có Thuộc tính bạn đang cố gắng đặt hàng, trong trường hợp đó bạn sẽ nhận được ngoại lệ.

EDIT: Ngoài ra, thay vì sử dụng Tìm, hãy thử sử dụng Đầu tiên.

PersonList.OrderBy(p => p.Attributes.First(a => a.Name == "Age").Value) 
+0

Tôi đã khắc phục lỗi đánh máy, phát hiện ra sự cố của tôi là với IQueryable và List, yêu cầu Cast. Cảm ơn bạn đã giúp đỡ. –

0

Tôi tưởng tượng rằng bạn đang nhận được ngoại lệ khi một mục không có thuộc tính tuổi. Tôi đã thử đoạn mã dưới đây, và nó hoạt động tốt - tôi đoán dữ liệu của bạn hơi lệch, như được chỉ ra bởi các áp phích khác. Dù sao, bên dưới hoạt động tốt ...

List<Person> personList = new List<Person>(); 
    Random rand = new Random(); 

    //generate 50 random persons 
    for (int i = 0; i < 50; i++) 
    { 
     Person p = new Person(); 
     p.Attributes = new List<PersonAttribute>(); 
     p.Attributes.Add(new PersonAttribute() { ID = 8, Name = "Age", Value = rand.Next(0, 100).ToString() }); 
     p.Attributes.Add(new PersonAttribute() { ID = 10, Name = "Name", Value = rand.Next(0, 100).ToString() }); 
     personList.Add(p); 
    } 

    var finalList = personList.OrderBy(c => c.Attributes.Find(a => a.Name == "Age").Value).ToList(); 
1

Giả định rằng lớp Thuộc tính triển khai IComparable hoặc có chức năng ToString tốt (tôi hy vọng).

var list = personList.OrderBy(p => p.Attributes.FirstOrDefault(a => a.Name == "Age")) 

Nếu cú ​​pháp được phức tạp hơn:

var list = personList 
      .OrderBy(p => 
        p.Attributes.FirstOrDefault(a => a.Name == "Age") == null ? 
        "" : p.Attributes.First(a => a.Name == "Age").Value 
      ); 

Tôi cũng giả định rằng bạn có một giá trị cho mỗi phím - nếu không bạn sẽ cần phải có mã thông minh hơn ... ;-)

9

OrderBy là phần mở rộng LINQ tạo ra chuỗi mới. Để đặt hàng chuỗi hiện tại, bạn cần phải thêm một phương thức tiện ích mở rộng hoặc hai ... sau đó bạn có thể sử dụng:

PersonList.Sort(p => p.Attributes.Find(
    s => s.Name == mySortAttribute).Value); 

public static class ListExtensions { 
    public static void Sort<TSource, TValue>(
    this List<TSource> source, 
    Func<TSource, TValue> selector) 
    { 
    var comparer = Comparer<TValue>.Default; 
    source.Sort((x, y) => comparer.Compare(selector(x), selector(y))); 
    } 
    public static void SortDescending<TSource, TValue>(
    this List<TSource> source, 
    Func<TSource, TValue> selector) 
    { 
    var comparer = Comparer<TValue>.Default; 
    source.Sort((x, y) => comparer.Compare(selector(y), selector(x))); 
    } 
} 
8

Tôi biết đây là một bài đăng cũ, nhưng tôi nghĩ tôi nên đăng so sánh tôi tìm thấy trong khi trước đây trong trường hợp bất cứ ai khác cần nó.

public class GenericComparer<T> : IComparer<T> 
{ 
    public string SortExpression { get; set; } 
    public int SortDirection { get; set; } // 0:Ascending, 1:Descending 

    public GenericComparer(string sortExpression, int sortDirection) 
    { 
     this.SortExpression = sortExpression; 
     this.SortDirection = sortDirection; 
    } 
    public GenericComparer() { } 

    #region IComparer<T> Members 
    public int Compare(T x, T y) 
    { 
     PropertyInfo propertyInfo = typeof(T).GetProperty(SortExpression); 
     IComparable obj1 = (IComparable)propertyInfo.GetValue(x, null); 
     IComparable obj2 = (IComparable)propertyInfo.GetValue(y, null); 

     if (SortDirection == 0) 
     { 
      return obj1.CompareTo(obj2); 
     } 
     else return obj2.CompareTo(obj1); 
    } 
    #endregion 
} 

Cách sử dụng

List<MyObject> objectList = GetObjects(); /* from your repository or whatever */ 
objectList.Sort(new GenericComparer<MyObject>("ObjectPropertyName", (int)SortDirection.Descending)); 
dropdown.DataSource = objectList; 
dropdown.DataBind(); 

Bạn có thể quá tải nhà xây dựng để chấp nhận các enum SortDirection. Tôi đã không làm điều này bởi vì các lớp học trong một thư viện mà không có một tham chiếu đến System.Web.

0

Một số trường hợp, bạn cần phải xem xét:

  • Kể từ khi thuộc tính của bạn là trong chuỗi thời đại "30" và "3" sẽ được đặt hàng trước một thời đại "4"
  • Thuộc tính có thể không tồn tại

Nếu bạn tạo này phương pháp khuyến nông lớp:

public static class ListExtenstions 
{ 
    public static List<Person> OrderList(this List<Person> list, string attributeName, PersonAttribute defaultAttribute) 
    { 
     return OrderList(list, attributeName, defaultAttribute, x => x); 
    } 

    public static List<Person> OrderList<T>(this List<Person> list, string attributeName, PersonAttribute defaultAttribute, Func<string, T> convertion) 
    { 
     return list.OrderBy(x => convertion((x.Attributes.FirstOrDefault(y => y.Name == attributeName) ?? defaultAttribute).Value)).ToList(); 

     // Query Syntax 
     //return 
     // (from p in list 
     //  let attribute = p.Attributes.FirstOrDefault(a => a.Name == attributeName) ?? defaultAttribute 
     //  orderby attribute.Value 
     //  select p).ToList(); 
    } 
} 

bạn có thể sau đó để rt danh sách chính xác theo cách này:

List<Person> persons = ... 
... 
PersonAttribute defaultAttribute = new PersonAttribute() { Value = "0" }; 
var ordered = persons.OrderList("Age", defaultAttribute, x => Convert.ToInt32(x)); 

Điều này sẽ cung cấp thứ tự sắp xếp chính xác. Nếu thuộc tính sẽ luôn xuất hiện, bạn có thể xóa defaultAttribute.

Để sắp xếp trên 'Tên' chỉ cần sử dụng:

List<Person> persons = ... 
... 
PersonAttribute defaultAttribute = new PersonAttribute() { Value = String.Empty }; 
var ordered persons.OrderList("Name", defaultAttribute); 
Các vấn đề liên quan