1

IComparableカスタムオブジェクトにインターフェースを実装して、List.Sort()アルファベット順にソートできるようにしようとしています。

私のオブジェクトに_nameは、文字列型であるというフィールドがあり、それに基づいてソートしたいと考えています。これが私が実装した方法です:

    public int CompareTo(object obj)
    {
        //Int reference table:
        //1 or greater means the current instance occurs after obj
        //0 means both elements occur in the same position
        //-1 or less means the current instance occurs before obj

        if (obj == null)
            return 1;

        Upgrade otherUpgrade = obj as Upgrade;

        if (otherUpgrade != null)
            return _name.CompareTo(otherUpgrade.Name);
        else
            throw new ArgumentException("Passed object is not an Upgrade.");
    }

私が何か間違ったことをしたのか、それとも単に文字列CompareToが機能するのかはわかりませんが、基本的に私のリストは次のようにソートされました:

  • アップグレードのテスト
  • テスト アップグレード 10
  • テスト アップグレード 11
  • テスト アップグレード 12
  • テスト アップグレード 13
  • テスト アップグレード 14
  • テスト アップグレード 15
  • テスト アップグレード 2
  • テスト アップグレード 3
  • テスト アップグレード 4
  • テスト アップグレード 5

私はそれらを次のようにソートしたい:

  • アップグレードのテスト
  • テスト アップグレード 2
  • テスト アップグレード 3
  • ...等
4

5 に答える 5

7

「辞書式」照合ではなく、「自然順序」(英語の慣習に精通した人間が選択する照合) が必要です。すべての文字に厳密な順序を割り当ててから、順番に各文字で並べ替えます。

ジェフは、問題を解決しようとするさまざまなアルゴリズムへのリンクを含む、いくつかの詳細についての良い記事をここに書いています。

http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html

Raymond は、ここで Windows がそれをどのように処理したかについて説明しました。

http://technet.microsoft.com/en-us/magazine/hh475812.aspx

基本的に問題は次のとおりです。自然順序照合には、人工知能の問題を解決する必要があります。人間が行うことをエミュレートしようとしていますが、それは驚くほど難しい場合があります。幸運を!

于 2013-02-01T00:57:54.653 に答える
5

文字列は辞書順でソートされます。すべての数値を同じ長さになるようにフォーマットするか (例: Test Upgrade 02)、比較子で数値を解析して比較ロジックに組み込む必要があります。

于 2013-02-01T00:51:11.013 に答える
3

これが発生する理由は、数値の明示的な知識がない文字列比較を行っているためです。各文字列は、各文字のそれぞれの文字コードによって順序付けられます。

必要な効果を得るには、もう少し作業が必要です。この質問を参照してください:数値を含む可能性のある文字列でソートする

于 2013-02-01T00:51:15.027 に答える
2

英数字ソート

public class AlphanumComparatorFast : IComparer
{
    public int Compare(object x, object y)
    {
    string s1 = x as string;
    if (s1 == null)
    {
        return 0;
    }
    string s2 = y as string;
    if (s2 == null)
    {
        return 0;
    }

    int len1 = s1.Length;
    int len2 = s2.Length;
    int marker1 = 0;
    int marker2 = 0;

    // Walk through two the strings with two markers.
    while (marker1 < len1 && marker2 < len2)
    {
        char ch1 = s1[marker1];
        char ch2 = s2[marker2];

        // Some buffers we can build up characters in for each chunk.
        char[] space1 = new char[len1];
        int loc1 = 0;
        char[] space2 = new char[len2];
        int loc2 = 0;

        // Walk through all following characters that are digits or
        // characters in BOTH strings starting at the appropriate marker.
        // Collect char arrays.
        do
        {
        space1[loc1++] = ch1;
        marker1++;

        if (marker1 < len1)
        {
            ch1 = s1[marker1];
        }
        else
        {
            break;
        }
        } while (char.IsDigit(ch1) == char.IsDigit(space1[0]));

        do
        {
        space2[loc2++] = ch2;
        marker2++;

        if (marker2 < len2)
        {
            ch2 = s2[marker2];
        }
        else
        {
            break;
        }
        } while (char.IsDigit(ch2) == char.IsDigit(space2[0]));

        // If we have collected numbers, compare them numerically.
        // Otherwise, if we have strings, compare them alphabetically.
        string str1 = new string(space1);
        string str2 = new string(space2);

        int result;

        if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
        {
        int thisNumericChunk = int.Parse(str1);
        int thatNumericChunk = int.Parse(str2);
        result = thisNumericChunk.CompareTo(thatNumericChunk);
        }
        else
        {
        result = str1.CompareTo(str2);
        }

        if (result != 0)
        {
        return result;
        }
    }
    return len1 - len2;
    }
}

使用法 :

using System;
using System.Collections;

class Program
{
    static void Main()
    {
    string[] highways = new string[]
    {
        "100F",
        "50F",
        "SR100",
        "SR9"
    };
    //
    // We want to sort a string array called highways in an
    // alphanumeric way. Call the static Array.Sort method.
    //
    Array.Sort(highways, new AlphanumComparatorFast());
    //
    // Display the results
    //
    foreach (string h in highways)
    {
        Console.WriteLine(h);
    }
    }
}

出力

50F
100F
SR9
SR100

于 2013-02-01T00:58:46.143 に答える
0

すべての返信ありがとうございます。私は独自の方法を実行しましたが、うまくいくようです。すべてのケースで機能するわけではありませんが、私のシナリオでは機能します。興味のある人のためのコードは次のとおりです。

    /// <summary>
    /// Compares the upgrade to another upgrade
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public int CompareTo(object obj)
    {
        //Int reference table:
        //1 or greater means the current instance occurs after obj
        //0 means both elements occur in the same position
        //-1 or less means the current instance occurs before obj

        if (obj == null)
            return 1;

        Upgrade otherUpgrade = obj as Upgrade;

        if (otherUpgrade != null)
        {
            //Split strings into arrays
            string[] splitStringOne = _name.Split(new char[] { ' ' });
            string[] splitStringTwo = otherUpgrade.Name.Split(new char[] { ' ' });

            //Will hold checks to see which comparer will be used
            bool sameWords = false, sameLength = false, bothInt = false;

            //Will hold the last part of the string if it is an int
            int intOne = 0, intTwo = 0;

            //Check if they have the same length
            sameLength = (splitStringOne.Length == splitStringTwo.Length);

            if (sameLength)
            {
                //Check to see if they both end in an int
                bothInt = (int.TryParse(splitStringOne[splitStringOne.Length - 1], out intOne) && int.TryParse(splitStringTwo[splitStringTwo.Length - 1], out intTwo));

                if (bothInt)
                {
                    //Check to see if the previous parts of the string are equal
                    for (int i = 0; i < splitStringOne.Length - 2; i++)
                    {
                        sameWords = (splitStringOne[i].ToLower().Equals(splitStringTwo[i].ToLower()));

                        if (!sameWords)
                            break;
                    }
                }
            }

            //If all criteria is met, use the customk comparer
            if (sameWords && sameLength && bothInt)
            {
                if (intOne < intTwo)
                    return -1;
                else if (intOne > intTwo)
                    return 1;
                else //Both equal
                    return 0;
            }
            //Else use the default string comparer
            else
                return _name.CompareTo(otherUpgrade.Name);            
        }
        else
            throw new ArgumentException("Passed object is not an Upgrade.");
    }

次のように、" " 文字を使用して間隔をあけた文字列に対して機能します。

テストデータ:

  • こんにちは11
  • こんにちは 2
  • こんにちは13

結果

  • こんにちは 2
  • こんにちは11
  • こんにちは13

Hello11やなどのデータHello2は分割できないため、機能しません。大文字と小文字を区別しません。

于 2013-02-01T13:13:24.953 に答える