使用查询表达式对List 进行排序

我使用Linq订购这样的结构时遇到问题:

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

一个人可能会这样:

 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" }; 

我想使用LINQ投影来对按我选择的person属性提升的人员列表进行排序,例如,对Age进行排序,或者对FirstName进行排序。

我正在尝试类似的东西

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

但语法失败了。 有什么线索吗?

OrderBy是一个产生新序列的LINQ扩展。 要订购现有序列,您需要添加一个或两个扩展方法…然后您可以使用:

 PersonList.Sort(p => p.Attributes.Find( s => s.Name == mySortAttribute).Value); public static class ListExtensions { public static void Sort( this List source, Func selector) { var comparer = Comparer.Default; source.Sort((x, y) => comparer.Compare(selector(x), selector(y))); } public static void SortDescending( this List source, Func selector) { var comparer = Comparer.Default; source.Sort((x, y) => comparer.Compare(selector(y), selector(x))); } } 

我知道这是一个很老的post,但我想我会发布一个我以前发现的比较器,万一其他人需要它。

 public class GenericComparer : IComparer { 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 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 } 

用法

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

您可以重载构造函数以接受SortDirection枚举。 我没有这样做,因为该类在没有System.Web引用的库中。

为什么不使用键值字典而不是List ? 我认为它会更适合,并使其他一切变得更容易。

更新 – 像这样:

 public class Person { public Dictionary Attributes = new Dictionary(); } List people = new List(); 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"]); 

这假设Attribute类实现IComparable或具有一个很好的ToString函数(我希望)。

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

否则语法变得更复杂:

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

我还假设每个键都有一个值 – 否则你需要更聪明的代码… 😉

可能是你的语法错了吗? 您的属性称为属性,但您在代码中使用了名为ObjectSettings的东西? 或者这是一个错字。

如果是,那么你的代码看起来很好,除非并非所有Person实例都有你想要的属性,在这种情况下你会得到一个例外。

编辑:此外,而不是使用查找,尝试使用第一。

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

我想你会得到一个例外,其中一个项目没有年龄属性。 我尝试了下面的代码,它运行正常 – 我猜你的数据有点偏,正如其他海报所指出的那样。 无论如何,以下工作正常……

  List personList = new List(); Random rand = new Random(); //generate 50 random persons for (int i = 0; i < 50; i++) { Person p = new Person(); p.Attributes = new List(); 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(); 

有些情况需要考虑:

  • 由于您的属性是字符串,因此在“4”年龄之前将订购年龄“30”和“3”
  • 该属性可能不存在

如果您创建此扩展方法类:

 public static class ListExtenstions { public static List OrderList(this List list, string attributeName, PersonAttribute defaultAttribute) { return OrderList(list, attributeName, defaultAttribute, x => x); } public static List OrderList(this List list, string attributeName, PersonAttribute defaultAttribute, Func 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(); } } 

然后,您可以以这种方式正确排序列表:

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

这将给出正确的排序顺序。 如果该属性始终存在,则可以删除defaultAttribute

要对“名称”进行排序,只需使用:

 List persons = ... ... PersonAttribute defaultAttribute = new PersonAttribute() { Value = String.Empty }; var ordered persons.OrderList("Name", defaultAttribute);