1

私はクラスを持っています:

public class Essay
    {   
            public int ID{get;set;}
            public string Name{get;set;}
    }

とエッセイタイプのリスト

List<Essay> essays=new List<Essay>();

name プロパティには、数字と文字が含まれています。

名前のプロパティでリストをソートしたい

例えば:

essays=
{1,"ccccc"},
{2,"aaaa"},
{3,"bbbb"},
{4,"10"},
{5,"1"},
{6,"2"},
{7,"1a"}

並べ替えたい:

essays=
{2,"aaaa"},
{3,"bbbb"},
{1,"ccccc"},
{5,"1"},
{7,"1a"},
{6,"2"},
{4,"10"}

どうすればいいですか?

すべてに感謝します。

4

4 に答える 4

4

名前には文字と数字が混在しているため、単純にアルファベット順に並べ替えると、並べ替えが難しくなります。

あなたがより好むと思われるのは自然な並べ替えと呼ばれ、テキスト内の数字は実際には数字として認識され、並べ替えは数値ベースでそれらを簡単に適用します。

このアルゴリズムのいくつかの実装は、Jeffs の Web ページにあります。

私が最も好むものはDaveのものです:

/*
 * 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
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
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;
        }
    }
}
于 2013-01-28T08:49:59.787 に答える
1

答えにはいくつかの要素があります。

最初の部分は、Sort()とラムダ比較メソッドを使用してリストをインプレースソートできることです。これは、IListの拡張メソッドとヘルパー「ComparisonDelegator」クラスを使用することで解決されます。これらを組み合わせることで、ラムダをList.Sort()に渡すことができます。

2番目の部分は、ここにある別の投稿(私が賛成した)と、この回答のAlphanumComparatorクラスに恥知らずに貼り付けたコードで対処されています。

(補足として、このスレッドの他の場所に投稿されたすべてのLinqの例は、リストのコピーを作成することを指摘しておく必要があります。これは短いリストには問題ありませんが、長いリストがあると、パフォーマンスの問題が発生する可能性があります。提示された解決策ここではリストのコピーは作成されません。)

すべてをまとめると、次のコードが出力されます。

ID=2, Name=aaaa
ID=3, Name=bbbb
ID=1, Name=ccccc
ID=5, Name=1
ID=7, Name=1a
ID=6, Name=2
ID=4, Name=10

そして、完全なコードサンプル(コンソールアプリケーションとしてコンパイル可能):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;


namespace Demo
{
    public static class Program
    {
        public static void Main()
        {
            var list = new List<Essay>
            {
                new Essay {ID=1, Name="ccccc"},
                new Essay {ID=2, Name="aaaa"},
                new Essay {ID=3, Name="bbbb"},
                new Essay {ID=4, Name="10"},
                new Essay {ID=5, Name="1"},
                new Essay {ID=6, Name="2"},
                new Essay {ID=7, Name="1a"}                
            };

            var comp = new AlphanumComparator();

            list.Sort((lhs, rhs) => comp.Compare(lhs.Name, rhs.Name));

            foreach (var essay in list)
            {
                Console.WriteLine("ID={0}, Name={1}", essay.ID, essay.Name);
            }
        }
    }

    public class Essay
    {
        public int ID
        {
            get;
            set;
        }

        public string Name
        {
            get;
            set;
        }
    }

    /// <summary>Extensions for IList{T}</summary>

    public static class ListExt
    {
        /// <summary> Sorts an IList{T} in place. </summary>

        public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
        {
            ArrayList.Adapter((IList)list).Sort(new ComparisonDelegator<T>(comparison));
        }
    }

    /// <summary>
    /// Provides a mechanism for easily converting a Comparison&lt;&gt; delegate (or lambda) to an IComparer&lt;&gt;.
    /// This can be used for List.BinarySearch(), for example.
    /// </summary>
    /// <typeparam name="T">The type of items to be compared.</typeparam>

    public sealed class ComparisonDelegator<T>: IComparer<T>, IComparer
    {
        /// <summary>Create from a Comparison&lt;&gt; delegate.</summary>
        /// <param name="comparison">A Comparison&lt;&gt; delegate.</param>

        public ComparisonDelegator(Comparison<T> comparison)
        {
            this._comparison = comparison;
        }

        /// <summary>Implements the IComparer.Compare() method.</summary>

        public int Compare(T x, T y)
        {
            return _comparison(x, y);
        }

        /// <summary>Implements the IComparer.Compare() method.</summary>

        public int Compare(object x, object y)
        {
            return _comparison((T)x, (T)y);
        }

        /// <summary>Used to store the Comparison delegate.</summary>

        private readonly Comparison<T> _comparison;
    }

    /// <summary>
    /// Special class to sort strings "naturally", 
    /// but to place non-numeric items *before* numeric items.
    /// </summary>

    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 if (char.IsDigit(thisChunk[0]) && !char.IsDigit(thatChunk[0]))
                {
                    return 1; // Ensure that non-numeric sorts before numeric.
                }
                else if (!char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
                {
                    return -1;  // Ensure that non-numeric sorts before numeric.
                }
                else
                {
                    result = thisChunk.ToString().CompareTo(thatChunk.ToString());
                }

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

            return 0;
        }
    }
}
于 2013-01-28T09:09:38.090 に答える
0

数値文字をより高いASCII値の文字に変更し、aを実行しorderby て元に戻すことができます。

List<Essay> essays = new List<Essay>();
        essays.Add(new Essay(){ID = 1, Name = "ccccc"});
        essays.Add(new Essay(){ID = 2, Name = "aaaa"});
        essays.Add(new Essay(){ID = 3, Name = "bbbb"});
        essays.Add(new Essay(){ID = 4, Name = "10"});
        essays.Add(new Essay(){ID = 5, Name = "1"});
        essays.Add(new Essay(){ID = 6, Name = "2"});
        essays.Add(new Essay(){ID = 7, Name = "1a"});

        essays.ForEach(q => Replace(q));
        var result = essays.OrderBy(q => q.Name).ToList();
        result.ForEach(q => Revert(q));

関連機能:

public void Replace(Essay x)
    {
        x.Name = x.Name.Replace('0', ((char)240));
        x.Name = x.Name.Replace('1', ((char)241));
        x.Name = x.Name.Replace('2', ((char)242));
        x.Name = x.Name.Replace('3', ((char)243));
        x.Name = x.Name.Replace('4', ((char)244));
        x.Name = x.Name.Replace('5', ((char)245));
        x.Name = x.Name.Replace('6', ((char)246));
        x.Name = x.Name.Replace('7', ((char)247));
        x.Name = x.Name.Replace('8', ((char)248));
        x.Name = x.Name.Replace('9', ((char)249));
    }

    public void Revert(Essay x)
    {
        x.Name = x.Name.Replace(((char)240), '0');
        x.Name = x.Name.Replace(((char)241), '1');
        x.Name = x.Name.Replace(((char)242), '2');
        x.Name = x.Name.Replace(((char)243), '3');
        x.Name = x.Name.Replace(((char)244), '4');
        x.Name = x.Name.Replace(((char)245), '5');
        x.Name = x.Name.Replace(((char)246), '6');
        x.Name = x.Name.Replace(((char)247), '7');
        x.Name = x.Name.Replace(((char)248), '8');
        x.Name = x.Name.Replace(((char)249), '9');


    }
于 2013-01-28T09:08:59.540 に答える
0

IComparer実装を作成し、OrderByを使用してリストを並べ替えました。正規表現を使用しての先頭の数字をキャプチャし、Name存在する場合はそれらを使用してアイテムを並べ替えます。そうでない場合は、文字列比較を使用して名前を比較します。数字で始まる名前は、常に文字で始まるすべての名前の後に続きます。それはあなたが要求した出力を与えます。

public class EssayNameComparer : IComparer<string>
{
    private Regex _digits = new Regex("(\\d+)(.*)");

    public int Compare(string a, string b)
    {
        Match matcha = _digits.Match(a);
        Match matchb = _digits.Match(b);

        if (matcha.Success && matchb.Success)
        {
            int numa = int.Parse(matcha.Groups[1].Value);
            int numb = int.Parse(matchb.Groups[1].Value);
            return numa.CompareTo(numb);
        }
        else if (matcha.Success)
        {
            return 1;
        }
        else if (matchb.Success)
        {
            return -1;
        }
        else
        {
            return string.Compare(a, b);
        }            
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        List<Essay> essays= new List<Essay>() { 
            new Essay { ID = 1, Name = "ccccc"},
            new Essay { ID = 2, Name = "aaaa"},
            new Essay { ID = 3, Name = "bbbb"},
            new Essay { ID = 4, Name = "10"},
            new Essay { ID = 5, Name = "1"},
            new Essay { ID = 6, Name = "2"},
            new Essay { ID = 7, Name = "1a"}
        };

        foreach(Essay essay in essays.OrderBy(e => e.Name, new EssayNameComparer()))
        {
            Console.WriteLine(essay.Name);
        }
    }
}

出力:

  • aaaa
  • bbbb
  • ccccc
  • 1
  • 1a
  • 2
  • 10
于 2013-01-28T09:10:24.283 に答える