Sort a List using query expressions

后端 未结 7 653
执念已碎
执念已碎 2021-02-05 15:34

I have a problem using Linq to order a structure like this :

public class Person
{
    public int ID { get; set; }
    public List Attribu         


        
相关标签:
7条回答
  • 2021-02-05 16:00

    Why don't you use a key-value dictionary instead of your List<PersonAttribute> ? It would suit better, i think, and make everything else easier.

    Update - like this:

    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"]);
    
    0 讨论(0)
  • 2021-02-05 16:03

    OrderBy is a LINQ extension that produces a new sequence. To order the existing sequence you need to add an extension method or two... then you can use:

    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)));
      }
    }
    
    0 讨论(0)
  • 2021-02-05 16:04

    Some cases you need to consider:

    • Since your attribute is in string an age of "30" and "3" will be ordered before an age of "4"
    • The attribute might not exist

    If you create this extension methods class:

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

    You can then sort the list correctly in this manner:

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

    This will give correct sorting order. If the attribute will always be present you could remove defaultAttribute.

    To sort on 'Name' just use:

    List<Person> persons = ...
    ...
    PersonAttribute defaultAttribute = new PersonAttribute() { Value = String.Empty };
    var ordered persons.OrderList("Name", defaultAttribute);
    
    0 讨论(0)
  • 2021-02-05 16:08

    This is assuming that Attribute class implement IComparable or has a nice ToString function (i hope).

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

    Otherwise the syntax gets more convoluted:

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

    I also assume that you have one value for each key - otherwise you'd need to have smarter code... ;-)

    0 讨论(0)
  • 2021-02-05 16:11

    I know this is an old post, but I thought I'd post a comparer I found a while ago in case anyone else needs it.

    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
     }
    

    Usage

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

    You could overload the constructor to accept the SortDirection enum. I didn't do this because the class is in a library without a reference to System.Web.

    0 讨论(0)
  • 2021-02-05 16:11

    I'd imagine that you're getting an exception where one item doesn't have an age attribute. I tried the below code, and it worked fine - I'm guessing your data is a bit off, as pointed out by other posters. Anyway, the below works fine...

        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();
    
    0 讨论(0)
提交回复
热议问题