2009-06-12 47 views
26

我有一个通用的使用自己的IComparer <T>使用LINQ的OrderBy

List<MyClass> 

其中MyClass具有包含值如属性InvoiceNumber

一分之二十零万零九百零六
二分之二十零万零九百零六
..
200906/10
200906/11
200906/12

我的目录绑定到

BindingList<T> 

它支持LINQ的排序:

protected override void ApplySortCore(
      PropertyDescriptor property, ListSortDirection direction) 
{ 

    _sortProperty = property; 
    _sortDirection = direction; 

    var items = this.Items; 

    switch (direction) 
    { 
     case ListSortDirection.Ascending: 
      items = items.OrderByDescending(x => property.GetValue(x)).ToList(); 
      break; 
     case ListSortDirection.Descending: 
      items = items.OrderByDescending(x => property.GetValue(x)).ToList(); 
      break; 
    } 

    this.Items = items; 

} 

但是默认的比较排序(如应该)是这样的:

一分之二十零万零九百零六
200906/10
200906/11
200906/1 2
200906/2

这在这种情况下是讨厌的。

现在我想用我自己的IComparer<T>这个。它看起来像这样:

public class MyComparer : IComparer<Object> 
{ 

    public int Compare(Object stringA, Object stringB) 
    { 
     String[] valueA = stringA.ToString().Split('/'); 
     String[] valueB = stringB.ToString().Split('/'); 

     if(valueA .Length != 2 || valueB .Length != 2) 
      return String.Compare(stringA.ToString(), stringB.ToString()); 

     if (valueA[0] == valueB[0]) 
     { 
      return String.Compare(valueA[1], valueB[1]); 
     } 
     else 
     { 
      return String.Compare(valueA[0], valueB[0]); 
     } 

    } 

} 

,改变了ApplySortCore代码使用此IComparer

case ListSortDirection.Ascending: 
    MyComparer comparer = new MyComparer(); 
    items = items.OrderByDescending(
       x => property.GetValue(x), comparer).ToList(); 
    break; 

当我调试我的代码,我看到MyComparer.Compare(object, object)被多次调用和返回正确的价值观( - 1,0,1)用于比较方法。

但我的名单仍然排序“错误”的方式。我错过了什么吗?我没有任何线索。

回答

17

您的比较器看起来不对我。你仍然只是在默认的文本排序中排序。当然,你希望被解析两个数字和排序依据是:

public int Compare(Object stringA, Object stringB) 
{ 
    string[] valueA = stringA.ToString().Split('/'); 
    string[] valueB = stringB.ToString().Split('/'); 

    if (valueA.Length != 2 || valueB.Length != 2) 
    { 
     stringA.ToString().CompareTo(stringB.ToString())); 
    } 

    // Note: do error checking and consider i18n issues too :) 
    if (valueA[0] == valueB[0]) 
    { 
     return int.Parse(valueA[1]).CompareTo(int.Parse(valueB[1])); 
    } 
    else 
    { 
     return int.Parse(valueA[0]).CompareTo(int.Parse(valueB[0])); 
    } 
} 

(请注意,这不符合你的问题,说明你已经通过调试和验证是比较返回正确的值坐好 - 但我恐怕我怀疑在这方面的人为错误。)

此外,斯文的权利 - 改变items的价值根本不会改变你的绑定列表。您应该添加:

this.Items = items; 

位于方法的底部。

+0

对不起,我shortend代码一点。在我原来的代码中,我做this.Items =项目; (如果没有,它不会排序)但int转换的作品(我一定是盲目的||愚蠢的错过了)。 Thx非常。 – 2009-06-12 11:06:15

2

排序的列表只绑定到局部变量项目,而不绑定到绑定列表的Items属性,因此它仍然未排序。

[编辑]基本上,你只是扔掉的结果你选的努力;-)

7

我遇到的一般自然排序的问题,并在博客这里的解决方案:

Natural Sort Compare with Linq OrderBy()

public class NaturalSortComparer<T> : IComparer<string>, IDisposable 
{ 
    private bool isAscending; 

    public NaturalSortComparer(bool inAscendingOrder = true) 
    { 
     this.isAscending = inAscendingOrder; 
    } 

    #region IComparer<string> Members 

    public int Compare(string x, string y) 
    { 
     throw new NotImplementedException(); 
    } 

    #endregion 

    #region IComparer<string> Members 

    int IComparer<string>.Compare(string x, string y) 
    { 
     if (x == y) 
      return 0; 

     string[] x1, y1; 

     if (!table.TryGetValue(x, out x1)) 
     { 
      x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)"); 
      table.Add(x, x1); 
     } 

     if (!table.TryGetValue(y, out y1)) 
     { 
      y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)"); 
      table.Add(y, y1); 
     } 

     int returnVal; 

     for (int i = 0; i < x1.Length && i < y1.Length; i++) 
     { 
      if (x1[i] != y1[i]) 
      { 
       returnVal = PartCompare(x1[i], y1[i]); 
       return isAscending ? returnVal : -returnVal; 
      } 
     } 

     if (y1.Length > x1.Length) 
     { 
      returnVal = 1; 
     } 
     else if (x1.Length > y1.Length) 
     { 
      returnVal = -1; 
     } 
     else 
     { 
      returnVal = 0; 
     } 

     return isAscending ? returnVal : -returnVal; 
    } 

    private static int PartCompare(string left, string right) 
    { 
     int x, y; 
     if (!int.TryParse(left, out x)) 
      return left.CompareTo(right); 

     if (!int.TryParse(right, out y)) 
      return left.CompareTo(right); 

     return x.CompareTo(y); 
    } 

    #endregion 

    private Dictionary<string, string[]> table = new Dictionary<string, string[]>(); 

    public void Dispose() 
    { 
     table.Clear(); 
     table = null; 
    } 
} 
5

您可以使用Alphanum算法:

(...) 
    items.OrderBy(x => property.GetValue(x), new AlphanumComparator()) 
    (...) 

AlphanumComparator

/* 
* The Alphanum Algorithm is an improved sorting algorithm for strings 
* containing numbers. Instead of sorting numbers in ASCII order like 
* a standard sort, this algorithm sorts numbers in numeric order. 
* 
* The Alphanum Algorithm is discussed at http://www.DaveKoelle.com 
* 
* Based on the Java implementation of Dave Koelle's Alphanum algorithm. 
* Contributed by Jonathan Ruckwood <[email protected]> 
* 
* Adapted by Dominik Hurnaus <[email protected]> to 
* - correctly sort words where one word starts with another word 
* - have slightly better performance 
* 
* Released under the MIT License - https://opensource.org/licenses/MIT 
* 
* Permission is hereby granted, free of charge, to any person obtaining 
* a copy of this software and associated documentation files (the "Software"), 
* to deal in the Software without restriction, including without limitation 
* the rights to use, copy, modify, merge, publish, distribute, sublicense, 
* and/or sell copies of the Software, and to permit persons to whom the 
* Software is furnished to do so, subject to the following conditions: 
* 
* The above copyright notice and this permission notice shall be included 
* in all copies or substantial portions of the Software. 
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 
* USE OR OTHER DEALINGS IN THE SOFTWARE. 
* 
*/ 
using System; 
using System.Collections; 
using System.Text; 

/* 
* Please compare against the latest Java version at http://www.DaveKoelle.com 
* to see the most recent modifications 
*/ 
namespace AlphanumComparator 
{ 
    public class AlphanumComparator : IComparer 
    { 
     private enum ChunkType {Alphanumeric, Numeric}; 
     private bool InChunk(char ch, char otherCh) 
     { 
      ChunkType type = ChunkType.Alphanumeric; 

      if (char.IsDigit(otherCh)) 
      { 
       type = ChunkType.Numeric; 
      } 

      if ((type == ChunkType.Alphanumeric && char.IsDigit(ch)) 
       || (type == ChunkType.Numeric && !char.IsDigit(ch))) 
      { 
       return false; 
      } 

      return true; 
     } 

     public int Compare(object x, object y) 
     { 
      String s1 = x as string; 
      String s2 = y as string; 
      if (s1 == null || s2 == null) 
      { 
       return 0; 
      } 

      int thisMarker = 0, thisNumericChunk = 0; 
      int thatMarker = 0, thatNumericChunk = 0; 

      while ((thisMarker < s1.Length) || (thatMarker < s2.Length)) 
      { 
       if (thisMarker >= s1.Length) 
       { 
        return -1; 
       } 
       else if (thatMarker >= s2.Length) 
       { 
        return 1; 
       } 
       char thisCh = s1[thisMarker]; 
       char thatCh = s2[thatMarker]; 

       StringBuilder thisChunk = new StringBuilder(); 
       StringBuilder thatChunk = new StringBuilder(); 

       while ((thisMarker < s1.Length) && (thisChunk.Length==0 ||InChunk(thisCh, thisChunk[0]))) 
       { 
        thisChunk.Append(thisCh); 
        thisMarker++; 

        if (thisMarker < s1.Length) 
        { 
         thisCh = s1[thisMarker]; 
        } 
       } 

       while ((thatMarker < s2.Length) && (thatChunk.Length==0 ||InChunk(thatCh, thatChunk[0]))) 
       { 
        thatChunk.Append(thatCh); 
        thatMarker++; 

        if (thatMarker < s2.Length) 
        { 
         thatCh = s2[thatMarker]; 
        } 
       } 

       int result = 0; 
       // If both chunks contain numeric characters, sort them numerically 
       if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0])) 
       { 
        thisNumericChunk = Convert.ToInt32(thisChunk.ToString()); 
        thatNumericChunk = Convert.ToInt32(thatChunk.ToString()); 

        if (thisNumericChunk < thatNumericChunk) 
        { 
         result = -1; 
        } 

        if (thisNumericChunk > thatNumericChunk) 
        { 
         result = 1; 
        } 
       } 
       else 
       { 
        result = thisChunk.ToString().CompareTo(thatChunk.ToString()); 
       } 

       if (result != 0) 
       { 
        return result; 
       } 
      } 

      return 0; 
     } 
    } 
} 
3

我们不能这样做:

public class MyComparer : IComparer<string> 
{ 

    public int Compare(string stringA, string stringB) 
    { 
     string small = stringA; 
     string big = stringB; 
     if (stringA.Length > stringB.Length) 
     { 
      small = stringB; 
      big = stringA; 
     } 
     else if (stringA.Length < stringB.Length) 
     { 
      small = stringA; 
      big = stringB; 
     } 
     for (int j = 0; j < small.Length; j++) 
     { 
      if (Convert.ToInt32(small[j]) > Convert.ToInt32(big[j])) return -1; 
      if (Convert.ToInt32(small[j]) < Convert.ToInt32(big[j])) return 1; 
     } 

     //big is indeed bigger 
     if (big.Length > small.Length) return 1; 

     //finally they are smae 
     return 0; 
    } 
} 

用法:

string[] inputStrings = {"_abc*&","#almnp","abc" }; 
//string[] inputStrings = { "#", "_", "_a", "@", "_" }; 
MyComparer computer = new MyComparer(); 
var kola = inputStrings.OrderBy(x => x, new MyComparer()).ToArray(); 

这是相同的:

Array.Sort(inputStrings, StringComparer.Ordinal);