4
namespace SortableLists
{
    using System;
    using System.Collections.Generic;

    public class Program
    {
        private static void Main() {
            var list = new List<ListItem>
                           {
                               new ListItem {AdmissionCode = "801r", Name = "Rajesh Koothrappali", RollNumber = 54},
                               new ListItem {AdmissionCode = "892k", Name = "Leonard Leakey Hofstadter", RollNumber = 34},
                               new ListItem {AdmissionCode = "1203a", Name = "Sheldon Lee Cooper", RollNumber = 46},
                               new ListItem {AdmissionCode = "802x", Name = "Howard Wolowitz", RollNumber = 98}
                           };
            list.ForEach(x => Console.WriteLine(x.RollNumber + ","+x.Name + "," + x.AdmissionCode));

            Console.Write("\n");
            list.Sort();
            list.ForEach(x => Console.WriteLine(x.RollNumber + "," + x.Name + "," + x.AdmissionCode));

            Console.ReadKey();
        }
    }

    public class ListItem : IComparable<ListItem>
    {
        public int RollNumber { get; set; }
        public string Name { get; set; }
        public string AdmissionCode { get; set; }

        #region Implementation of IComparable<in ListItem>

        public int CompareTo(ListItem other) {
            return AdmissionCode.CompareTo(other.AdmissionCode);
        }

        #endregion
    }
}

ソート後にアドミッションコード1203シェルドン博士がリストの一番上に表示されるのは、これがどのようなソートなのかわかりません??? 私は 801、802、803、1203 を期待していました... 誰か説明できますか?

4

5 に答える 5

4

比較している数字は数字としてではなく、文字列として扱われます。また、文字列の場合、文字「1」は「8」の前に来るため、テキストとして扱われる場合は順序が異なるため、大きい数字が最初に表示されます。

intこのフィールドを1つとして扱いたい場合は、このフィールドをに変換することをお勧めします。

編集:編集した質問(フィールドには文字も含まれるようになりました)の場合、目的の順序で比較するためのカスタム比較ロジックを作成する必要があります。

たとえば、ロジックを次のようにしたいと思います。

  1. コードを数字と文字に分割します。
  2. 数値のみを比較します(intとして)。
  3. 数値部分が両方の値で同じである場合は、残りを文字列として比較します。

このロジック(または本当に必要なロジック)をCompareToメソッドに実装すると、希望する順序になります。

このようなロジックのコードは次のようになります。

public class ListItem : IComparable<ListItem>
{
    public int RollNumber { get; set; }
    public string Name { get; set; }
    public string AdmissionCode { get; set; }

    private static readonly char[] Numbers = new[]
    {
        '0',
        '1',
        '2',
        '3',
        '4',
        '5',
        '6',
        '7',
        '8',
        '9'
    };

    #region Implementation of IComparable<in ListItem>
    public int CompareTo(ListItem other)
    {
        // Assumes AdmissionCode is in ####ABC format,
        // with at least one number and any amount of letters.
        string myNumberPart, myRemainingPart;
        string otherNumberPart, otherRemainingPart;

        SplitAdmissionCode(AdmissionCode, out myNumberPart, out myRemainingPart);
        SplitAdmissionCode(other.AdmissionCode, out otherNumberPart, out otherRemainingPart);

        int myNumber = int.Parse(myNumberPart);
        int otherNumber = int.Parse(otherNumberPart);

        int result = myNumber.CompareTo(otherNumber);

        // Numbers are different.
        if (result != 0)
            return result;

        // Numbers are same. Use text compare for the remaining part.
        return myRemainingPart.CompareTo(otherRemainingPart);
    }

    private void SplitAdmissionCode(string code, out string numbersPart, out string remainingPart)
    {
        int lastNumberIndex = code.LastIndexOfAny(Numbers);

        numbersPart = code.Substring(0, lastNumberIndex + 1);

        if (lastNumberIndex == code.Length - 1)
            remainingPart = "";
        else
            remainingPart = code.Substring(lastNumberIndex + 1);
    }
    #endregion
}
于 2011-01-11T08:10:06.827 に答える
3

数値ではなく文字列を並べ替えています。文字列CompareToは長さを考慮していません。

編集(編集した質問の場合):数字で始まる文字列を並べ替える場合、CompareToメソッドの並べ替えでは、アルファベット順であるため、期待どおりの結果が得られません。

于 2011-01-11T08:09:02.757 に答える
0

文字列「1203」は「801」未満です。比較する前に文字列を数値に変換しようとする場合があります(本質的に数値を表す場合)

于 2011-01-11T08:11:33.670 に答える
0

シンプルに保ちたい場合:

public class ListItem : IComparable<ListItem>
{
    public int RollNumber { get; set; }
    public string Name { get; set; }
    public string AdmissionCode { get; set; }

    #region Implementation of IComparable<in ListItem>

    public int CompareTo(ListItem other)
    {
        return this.AdmissionCode.Length != other.AdmissionCode.Length
            ? this.AdmissionCode.Length.CompareTo(other.AdmissionCode.Length)
            : this.AdmissionCode.CompareTo(other.AdmissionCode);
    }

    #endregion
}
于 2011-01-11T09:55:50.630 に答える
0

他の人が指摘しているように、これは期待される機能です。
注文が必要な場合は、これを801r, 802x, 892k, 1203a行います

public class ListItem : IComparable<ListItem>
{
    public int RollNumber { get; set; }
    public string Name { get; set; }
    public string AdmissionCode { get; set; }

    public int CompareTo(ListItem other) {
        return ExtractNumbers(this.AdmissionCode).CompareTo(ExtractNumbers(other.AdmissionCode));
    }
    private int ExtractNumbers(string expr) {
        return Convert.ToInt32(String.Join(null,System.Text.RegularExpressions.Regex.Split(expr, "[^\\d]")));
    }
}
于 2011-01-11T08:33:25.650 に答える