LINQ 自定义排序

Posted

技术标签:

【中文标题】LINQ 自定义排序【英文标题】:LINQ Custom Sort 【发布时间】:2014-09-10 13:06:15 【问题描述】:

我想要一个字母排序,但有一个例外。 我首先需要一个名称 =“Public”且 ID =“0”的组。 (宁愿使用 ID = 0) 之后,然后按名称对其余部分进行排序。 这不会首先公开。

public IEnumerable<GroupAuthority> GroupAuthoritysSorted 
 
   get 
    
       return GroupAuthoritys.OrderBy(x => x.Group.Name); 
    

我想要的是:

return GroupAuthoritys.Where(x => x.ID == 0)
       UNION 
       GroupAuthoritys.Where(x => x.ID > 0).OrderBy(x => x.Group.Name);

GroupAuthority 有一个公共属性 Group,Group 有公共属性 ID 和 Name。

我基本上使用了公认的答案

using System.ComponentModel;

namespace SortCustom

    public partial class MainWindow : Window
    
        public MainWindow()
        
            InitializeComponent();
            TestSort();
        
        private void TestSort()
        
            List<CustomSort> LCS = new List<CustomSort>();
            LCS.Add(new CustomSort(5, "sss"));
            LCS.Add(new CustomSort(6, "xxx"));
            LCS.Add(new CustomSort(4, "xxx"));
            LCS.Add(new CustomSort(3, "aaa"));
            LCS.Add(new CustomSort(7, "bbb"));
            LCS.Add(new CustomSort(0, "pub"));
            LCS.Add(new CustomSort(2, "eee"));
            LCS.Add(new CustomSort(3, "www"));
            foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
            LCS.Sort();
            foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
        
    
    public class CustomSort : Object, INotifyPropertyChanged, IComparable<CustomSort>
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(PropertyChangedEventArgs e)
        
            if (PropertyChanged != null) PropertyChanged(this, e);
        
        private Int16 id;
        private string name;
        public Int16 ID  get  return id;  
        public String Name  get  return name;  
        public int CompareTo(CustomSort obj)
        
            if (this.ID == 0) return -1;
            if (obj == null) return 1;
            if (obj is CustomSort)
            
                CustomSort comp = (CustomSort)obj;
                if (comp.ID == 0) return 1;
                return string.Compare(this.Name, comp.Name, true);
            
            else
            
                return 1;
            
        
        public override bool Equals(Object obj)
        
            // Check for null values and compare run-time types.
            if (obj == null) return false;
            if (!(obj is CustomSort)) return false;
            CustomSort comp = (CustomSort)obj;
            return (comp.ID == this.ID); 
        
        public override int GetHashCode()
        
            return (Int32)ID;
        
        public CustomSort(Int16 ID, String Name)
        
            id = ID;
            name = Name;
        
    

【问题讨论】:

告诉我们class GroupAuthority的定义。 @ClickRick GroupAuthority 有一个公共属性 Group 并且 Group 有公共属性 ID 和 Name。 请将其编辑到您的问题中。 GroupAuthoritys.OrderBy(x => x.Group.Name == "Public" && x.Group.ID == 0 ? 0 : 1, x.Group.Name); 在任何手机上,无法准确记住语法,但这是完成它的基本思路 【参考方案1】:

您需要使用比较函数,它们是从您的类型的两个实例返回一个整数的函数,如果两者相等则返回 0,如果第一个小于第二个则返回负值,如果第一个返回正值大于秒。

MSDN 有一个nice table,比文字更容易理解(*** 在 2014 年仍然不支持表格)

IComparer&lt;T&gt;

大多数排序方法都接受IComparer&lt;T&gt; 类型的自定义比较器实现,您应该创建一个封装Group 的自定义规则:

class GroupComparer : IComparer<Group>

    public int Compare(Group a, Group b)
    
        if (a != null && b != null && (a.Id == 0 || b.Id == 0))
        
            if (a.Id == b.Id)
            
                // Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consitent
                return 0; 
            
            return a.Id == 0 ? -1 : 1;
        

        if (a == null || b == null)
        
            if (ReferenceEquals(a, b))
            
                return 0;
            
            return a == null ? -1 : 1;
        
        return Comparer<string>.Default.Compare(a.Name, b.Name);
    

用法:

items.OrderBy(_ => _, new GroupAuthorityComparer());

IComparable&lt;T&gt;

如果这是比较 Group 实例的唯一方法,您应该让它实现 IComparable&lt;T&gt;,这样如果有人想对您的类进行排序,则不需要额外的代码:

class Group : IComparable<Group>

    ...

    public int CompareTo(Group b)
    
        if (b != null && (Id == 0 || b.Id == 0))
        
            if (Id == b.Id)
            
                // Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consitent
                return 0; 
            
            return Id == 0 ? -1 : 1;
        

        return Comparer<string>.Default.Compare(Name, b.Name);
       

用法:

items.OrderBy(_ => _.Group);

应根据使用此特定比较器的位置来选择一种方式或另一种方式:是此类项目的主要排序还是仅应在一种特定情况下使用的排序,例如仅在一些管理视图。

您甚至可以更上一层并提供IComparable&lt;GroupAuthority&gt; 实现(一旦集团实现IComparable&lt;Group&gt; 就很容易):

class GroupAuthority : IComparable<GroupAuthority>

    ...

    public int CompareTo(GroupAuthority b)
    
        return Comparer<Group>.Default.Compare(Group, b.Group);
    

用法:

items.OrderBy(_ => _);

最后一个的优点是它会被自动使用,所以像GroupAuthoritys.ToList().Sort()这样的代码会开箱即用。

【讨论】:

【参考方案2】:

你可以试试这样的

list.Sort((x, y) =>

    if (x.Id == 0)
    
        return -1;
    
    if (y.Id == 0)
    
        return 1;
    

    return x.Group.Name.CompareTo(y.Group.Name);
);

列表是List&lt;T&gt;

此方法利用List&lt;T&gt; 使用Comparison&lt;T&gt; 委托提供的自定义排序选项。

基本上这个方法的作用是,它只是在Id时添加特殊的比较条件,如果它是零,它会返回一个值,表明对象更小,这使得对象进入列表的顶部。如果不是,它会使用其Group.Name 属性按升序对对象进行排序。

【讨论】:

这正在增加投票,我当然希望我能理解它。请解释一下。 @Blam 添加了解释,抱歉耽搁了。我是时候离开办公室了:( 我没有投反对票。我打算试试看。 @Blam 谢谢,我在发布之前进行了测试,如果有什么让我知道我会调查的。 这工作 +1。为什么你认为这个问题是-2?【参考方案3】:
    public IEnumerable<GroupAuthority> GroupAuthoritysSorted 
     
        get 
         
            return GroupAuthoritys.OrderBy(x => x.Group.ID == 0)
                                  .ThenBy(x => x.Group.Name); 
         
    

【讨论】:

以上是关于LINQ 自定义排序的主要内容,如果未能解决你的问题,请参考以下文章

HashSet使用自定义IComparer排序而不使用LINQ

C# 自定义类型数组的排序

对自定义类列表进行排序<T>

MySql 自定义排序

MySQL 根据自定义规则排序

Excel多条件自定义排序