使用查询表达式</t>对List <t>进行排序

时间:2009-03-30 03:18:07

标签: c# linq sorting expression

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

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

一个人可能会这样:

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);

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

7 个答案:

答案 0 :(得分:9)

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

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

答案 1 :(得分:8)

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

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
 }

<强>用法

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

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

答案 2 :(得分:5)

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

更新 - 像这样:

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"]);

答案 3 :(得分:1)

这假设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
            );

我还假设每个键都有一个值 - 否则你需要更聪明的代码......; - )

答案 4 :(得分:0)

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

如果是,那么您的代码看起来很好,除非并非所有Person实例都具有您尝试订购的属性,在这种情况下您将获得异常。

编辑: 此外,请尝试使用First。

,而不是使用Find
PersonList.OrderBy(p => p.Attributes.First(a => a.Name == "Age").Value)

答案 5 :(得分:0)

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

    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();

答案 6 :(得分:0)

有些情况需要考虑:

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

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

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();
    }
}

然后,您可以通过以下方式正确排序列表:

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

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

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

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