2

次のソートされていないリストがあります。

List<string> myUnsortedList = New List<string>();

myUnsortedList.Add("Alpha");
myUnsortedList.Add("(avg) Alpha");
myUnsortedList.Add("Zeta");
myUnsortedList.Add("Beta");
myUnsortedList.Add("(avg) Beta");
myUnsortedList.Add("(avg) Zeta");

リストをアルファベットの降順でソートし、通常の値の直後に (avg) の値を入れたい:

最終結果: ゼータ、(平均) ゼータ、ベータ、(平均) ベータ、アルファ、(平均) アルファ

私のアプリケーションは書かれており、ソートを達成するC#ために使用したいLINQ

4

8 に答える 8

6

「(avg)」が唯一の特別な接頭辞であると仮定すると、これは必要なものに対して問題なく機能するはずです

これにより、「(avg)」を含まないすべての文字列が降順で並べ替えられ、文字列の長さで並べ替えられます。

var result = myUnsortedList.OrderByDescending(x => x.Replace("(avg) ", "")).ThenBy(x => x.Length);

最終結果:

  • ゼータ
  • (平均) ゼータ
  • ベータ
  • (平均) ベータ
  • アルファ
  • (平均) アルファ
于 2012-12-28T02:29:14.583 に答える
3

LINQ を使用してこれを実行する方法をいくつか示します。また、提示した順序とは異なる順序で値が発生した場合に、値を正しく並べ替えます。たとえば、"(avg) Zeta" が "Zeta" の前にある場合、ソート後は後者が最初に来るはずです。

上記の内容に合わせて並べ替えたサンプル リストを次に示します。

var myUnsortedList = new List<string>
{
    "Alpha",
    "(avg) Alpha",
    "(avg) Zeta",
    "Zeta",
    "Beta",
    "(avg) Beta"
};

ラムダ構文

string prefix = "(avg)";
var result = myUnsortedList.Select(s => new
                           {
                               Value = s,
                               Modified = s.Replace(prefix, "").TrimStart(),
                               HasPrefix = s.StartsWith(prefix)
                           })
                           .OrderByDescending(o => o.Modified)
                           .ThenBy(o => o.HasPrefix)
                           .Select(o => o.Value);

ジップ・アグリゲート

string prefix = "(avg)";
var avg = myUnsortedList.Where(o => o.StartsWith(prefix))
                        .OrderByDescending(o => o);
var regular = myUnsortedList.Where(o => !o.StartsWith(prefix))
                            .OrderByDescending(o => o);
var result = regular.Zip(avg, (f, s) => new { First = f, Second = s })
                    .Aggregate(new List<string>(), (list, o) =>
                                   new List<string>(list) { o.First, o.Second });

クエリ構文と文字列分割

これはラムダ構文に似てprefixいますが、接頭辞を持つ文字列を判別するために を使用していない点が異なります。代わりに、スペースで分割しています。分割結果に複数の項目がある場合は、プレフィックスがあると想定しています。次に、値とプレフィックスの可用性に基づいて注文します。

var result = from s in myUnsortedList
             let split = s.Split(' ')
             let hasPrefix = split.Length > 1
             let value = hasPrefix ? split[1] : s
             orderby value descending, hasPrefix
             select s;
于 2012-12-28T03:19:29.560 に答える
1

おそらく独自のカスタムを作成する必要がありますIComparer<T>

class MyCustomComparer : IComparer<string>
{
    private readonly StringComparison StringComparer;

    public static readonly MyCustomComparer Ordinal =
        new MyCustomComparer(StringComparison.Ordinal);
    public static readonly MyCustomComparer OrdinalIgnoreCase =
        new MyCustomComparer(StringComparison.OrdinalIgnoreCase);
    // etc.

    private MyCustomComparer(StringComparison stringComparer)
    {
        StringComparer = stringComparer;
    }

    public int Compare(string x, string y)  
    {  
        bool isMatchedX = IsMatchedPattern(x);
        bool isMatchedY = IsMatchedPattern(y);

        if (isMatchedX&& !isMatchedY ) // x matches the pattern.
        {
            return String.Compare(Strip(x), y, StringComparer);
        }
        if (isMatchedY && !isMatchedX) // y matches the pattern.
        {
            return String.Compare(Strip(y), x, StringComparer);
        }

        return String.Compare(x, y, StringComparison.Ordinal);
    }

    private static bool isMatchedPattern(string str)
    {
        // Use some way to return if it matches your pattern.
        // StartsWith, Contains, Regex, etc.
    }

    private static string Strip(string str)
    {
        // Use some way to return the stripped string.
        // Substring, Replace, Regex, etc.
    }
}

xyがパターンに一致するかどうかを確認します。どちらか、または両方が一致しない場合は、標準の比較操作を使用します。基本的に、1 つ (そして 1 つだけ) がパターンに一致する場合にのみカスタム比較操作が必要です。

xがパターンに一致し、yが一致しない場合、x削除し、操作を使用してyに対してxの削除されたバージョンをチェックします。yがパターンに一致し、xが一致しない場合は、y削除し、操作を使用してxに対してyの削除されたバージョンをチェックします。String.Compare(...)String.Compare(...)

StringComparisonケース/カルチャオプションのカスタム比較子の静的読み取り専用インスタンスを公開することで、動作をコピーする方法を示すために回答を更新しました。

最後に、カスタム比較子で LINQ を使用します。myList.OrderBy(x => x, MyCustomComparer.Ordinal);


最後に 1 つ…必要に応じて最適化してください。これは私の気まぐれでテストされていないコードです。ロジックはあると思います。ただし、タイプミスが発生する可能性があります。

それが役立つことを願っています。

于 2012-12-28T02:52:24.140 に答える
1

リストを 2 つのリストに分割します。1 つは通常、もう 1 つは平均です。両方並べます。

次に、手動の「Zipper Merge」を実行します。

于 2012-12-28T02:49:25.433 に答える
0

linq の順序付けで独自のロジックを使用するには、独自の Comparer を実装し、そのインスタンスを以下のような linq メソッドの 2 番目のパラメーターとして使用する必要がありOrderByますOrderByDescending

namespace ConsoleApplication71
{
    public class AVGComparer : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            // Null checkings are necessary to prevent null refernce exceptions
            if((x == null) && (y == null)) return 0;
            if(x == null) return -1;
            if(y == null) return 1;

            const string avg = @"(avg) ";

            if(x.StartsWith(avg) || y.StartsWith(avg))
            {
                return x.Replace(avg, string.Empty).CompareTo(y.Replace(avg, string.Empty));
            }

            return x.CompareTo(y);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<string> myUnsortedList = new List<string>();

            myUnsortedList.Add("Alpha");
            myUnsortedList.Add("(avg) Alpha");
            myUnsortedList.Add("Zeta");
            myUnsortedList.Add("Beta");
            myUnsortedList.Add("(avg) Beta");
            myUnsortedList.Add("(avg) Zeta");

            var mySortedList = myUnsortedList.OrderByDescending(s => s, new AVGComparer());

            foreach (string s in mySortedList)
            {
                Console.WriteLine(s);
            }
        }
    }
}

出力は次のとおりです。

Zeta
(avg) Zeta
Beta
(avg) Beta
Alpha
(avg) Alpha
于 2012-12-28T02:56:39.807 に答える
0

これには間違ったデータ構造を使用しているように感じます。SortedDictionary を使用して、「name => avg」にしないのはなぜですか

テストされていない、おそらく動作するコード:

SortedDictionary<string, int> dict = new SortedDictionary<string, int>();
dict.Add("Alpha", 10);
dict.Add("Beta", 20);
dict.Add("Zeta", 30);

foreach(string key in dict.Keys.Reverse())
{
   int avg = dict[key];
}
于 2012-12-28T02:42:24.860 に答える
0

一行に:

var sorted = myUnsortedList.OrderByDescending(x => x.Replace("(avg) ", "")).ThenBy(x=> x.Contains("(avg)")).ToList();

これが合格テストです(nunit):

[Test]
public void CustomSort()
{
    var myUnsortedList = new List<string> { "Zeta", "Alpha", "(avg) Alpha", "Beta", "(avg) Beta", "(avg) Zeta" };
    var EXPECTED_RESULT = new List<string> { "Zeta", "(avg) Zeta", "Beta", "(avg) Beta", "Alpha", "(avg) Alpha" };

    var sorted = myUnsortedList.OrderByDescending(x => x.Replace("(avg) ", "")).ThenBy(x=> x.Contains("(avg)")).ToList();

    for (int i = 0; i < myUnsortedList.Count; i++)
    {
        Assert.That(sorted[i], Is.EqualTo(EXPECTED_RESULT[i]));
    }
}
于 2012-12-28T03:32:34.220 に答える
0

別の方法は、実装してから次のように言ういくつかの比較子を実装することMyComparerですIComparer<string>

var result = myUnsortedList.OrderBy(x => x, new MyComparer());
于 2012-12-28T02:29:32.820 に答える