简体   繁体   English

使用自己的IComparer <T> 与Linq OrderBy

[英]Use own IComparer<T> with Linq OrderBy

I have a generic 我有一个通用的

List<MyClass>

where MyClass has a property InvoiceNumber which contains values such as: 其中MyClass有一个属性InvoiceNumber ,其中包含如下值:

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

My list is bound to a 我的名单必然是一个

BindingList<T>

which supports sorting with linq: 支持使用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;

}

However the default comparer sorts (as supposed) like this: 但是默认的比较器排序(假设)如下:

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

which is nasty in this case. 在这种情况下这是令人讨厌的。

Now I want to use my own IComparer<T> with this. 现在我想使用我自己的IComparer<T> It looks like this: 它看起来像这样:

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

    }

}

and changed the ApplySortCore code to use this IComparer : 并更改了ApplySortCore代码以使用此IComparer

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

When I debug my code, I see that MyComparer.Compare(object, object) is called multiple times and returns the right values (-1, 0, 1) for a compare method. 当我调试我的代码时,我看到MyComparer.Compare(object, object)被多次调用,并为compare方法返回正确的值(-1,0,1)。

But my list is still sorted the "wrong" way. 但我的清单仍然是“错误”的方式。 Am I missing something? 我错过了什么吗? I have no clue. 我没有线索。

Your comparer looks wrong to me. 你的比较对我来说不对劲。 You're still just sorting in the default text ordering. 您仍然只是在默认文本排序中排序。 Surely you want to be parsing the two numbers and sorting based on that: 当然你想要解析这两个数字并根据它进行排序:

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

(Note that this doesn't sit well with your question stating that you've debugged through and verified that Compare is returning the right value - but I'm afraid I suspect human error on that front.) (请注意,这并不适合您的问题,说明您已通过调试并验证了Compare正在返回正确的值 - 但我担心我会怀疑此前面的人为错误。)

Additionally, Sven's right - changing the value of items doesn't change your bound list at all. 此外,Sven的权利 - 更改items的值根本不会改变您的绑定列表。 You should add: 你应该添加:

this.Items = items;

at the bottom of your method. 在你的方法的底部。

I encountered the issue of general natural sorting and blogged the solution here: 我遇到了一般自然排序的问题,并在此处发布了解决方案:

Natural Sort Compare with Linq OrderBy() 自然排序与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;
    }
}

You can use The Alphanum Algorithm: 你可以使用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 <jonathan.ruckwood@gmail.com>
 *
 * Adapted by Dominik Hurnaus <dominik.hurnaus@gmail.com> 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;
        }
    }
}

Can't we do like this: 我们不能这样做:

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

Usage: 用法:

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

This is same as : 这与:

    Array.Sort(inputStrings, StringComparer.Ordinal);

The sorted list is only bound to the local variable items, not to the Items property of your binding list, hence it remains unsorted. 排序列表仅绑定到局部变量项,而不绑定到绑定列表的Items属性,因此它保持未排序。

[Edit] Basically, you're simply throwing away the result of your sorting efforts ;-) [编辑]基本上,你只是丢掉你的分类工作的结果;-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM