5

次のコードを書きました。これは、ネイティブで文字列を並べ替えますが、既定の関数よりも優先されるstring.Compare()例外のコレクション (この場合は ) を許可します。customPrioritystring.Compare()

それはすべて少し長くなっているように思えます.これを可能にするために.NETに何かが組み込まれているのではないかと思っていましたか?

    var unorderered = new[] { "a", "b", "c", "x", "y", "z" };
    var ordered = unorderered.OrderBy(a => a, new CustomStringComparer());
    //expected order y,x,a,b,c,z

class CustomStringComparer : IComparer<string>
{
    int IComparer<string>.Compare(string x, string y)
    {
        if (x == y)
            return 0;
        else
        {
            //----------------------------
            //beginning of custom ordering
            var customPriority = new[] { "y", "x" };
            if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array
            {
                if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y))
                    return -1;                   
                return 1;
            }
            else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x)                    
                return -1;
            else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y)                    
                return 1;
            //---------------------------
            //degrade to default ordering
            else
                return string.Compare(x, y);

        }
    }
}
4

3 に答える 3

4

まず、問題を言い換えると便利だと思います。次のように並べ替えます。

  1. 指定された配列のインデックス。アイテムが配列にない場合、インデックスは無限大です
  2. 文字列自体

つまりOrderBy()、最初の条件に使用し、次にThenBy()2番目の条件に使用することで、ソート順を達成できます。

private static uint NegativeToMaxValue(int i)
{
    if (i < 0)
        return uint.MaxValue;
    return (uint)i;
}

…

var ordered = unorderered
    .OrderBy(a => NegativeToMaxValue(Array.IndexOf(new[] { "y", "x" }, a)))
    .ThenBy(a => a);

NegativeToMaxValue()配列にない項目は最後である必要があるため必要ですが、インデックスが-1であるため、通常は最初になります。IndexOf()(同じことを行うためのハックで読めない方法は、の結果を直接キャストすることですuint。)

を作成してこの並べ替えを再利用したい場合IComparerは、.Netにはそれを支援するものは何もないと思います。ただし、代わりにComparerExtensionsを使用できます。

IComparer<string> comparer = KeyComparer<string>
    .OrderBy(a => NegativeToMaxValue(Array.IndexOf(new[] { "y", "x" }, a)))
    .ThenBy(a => a);
于 2013-02-05T13:27:02.963 に答える
1

あなたが望むことを行うための組み込みの比較方法はありませんが、それはあなたが話している「長ったらしい」部分ではないと思います。

煩わしいのは、単純な比較関数であるべきものを渡すためだけにカスタム比較クラスを作成しなければならないことです。

さて、それを軽減する方法があります。メソッドの名前を渡すだけで OrderBy() を使用できるいくつかのヘルパー クラスを作成できます。これらのクラスを作成すると、すべての OrderBy() ステートメントで機能します。

ここにいくつかのサンプルコードがあります。ヘルパー クラスは、EnumerableExt および ComparisonDelegator と呼ばれます。これらは連携して、メソッドを OrderBy() に渡すことができます。

以下のコードは明らかにあなたのコードよりもはるかに長いですが、EnumerableExt クラスと ComparisonDelegator クラスは別の共通アセンブリにあるので、それらを数えないでください。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;

namespace Demo
{
    public static class Program
    {
        private static void Main(string[] args)
        {
            var unorderered = new[] { "a", "b", "c", "x", "y", "z" };

            var ordered = unorderered.OrderBy(compare); // Just need to specify the compare method!
        }

        // Each custom compare method must be written specially, as before:

        private static int compare(string x, string y)
        {
            if (x == y)
                return 0;
            else
            {
                //----------------------------
                //beginning of custom ordering
                var customPriority = new[] { "y", "x" };
                if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array
                {
                    if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y))
                        return -1;
                    return 1;
                }
                else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x)                    
                    return -1;
                else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y)                    
                    return 1;
                //---------------------------
                //degrade to default ordering
                else
                    return string.Compare(x, y);

            }
        }
    }

    // The following classes only need to be written once:

    public static class EnumerableExt
    {
        /// <summary>
        /// Convenience method on IEnumerable{T} to allow passing of a
        /// Comparison{T} delegate to the OrderBy method.
        /// </summary>

        public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison)
        {
            Contract.Requires(list != null, "list can't be null.");
            Contract.Requires(comparison != null, "comparer can't be null.");

            return list.OrderBy(t => t, 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)
        {
            Contract.Requires(comparison != null);

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

次に、次のように比較メソッドをインラインで記述することもできます (ただし、このような複雑な比較メソッドについてはお勧めしません。これは説明のためだけです)。

    private static void Main(string[] args)
    {
        var unorderered = new[] { "a", "b", "c", "x", "y", "z" };

        var ordered = unorderered.OrderBy((x, y) =>
        {
            if (x == y)
                return 0;
            else
            {
                var customPriority = new[] { "y", "x" };
                if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array
                {
                    if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y))
                        return -1;
                    return 1;
                }
                else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x)                    
                    return -1;
                else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y)                    
                    return 1;
                else
                    return string.Compare(x, y);
            }
        });
    }
于 2013-02-05T12:56:41.873 に答える
0

私は 99.99% 確信しています。.Net Framework にはデフォルトでそのようなものは存在しません。


並べ替えは非常にカスタマイズされており、一般的な並べ替えの方法ではないため、.NET Framework には既定でそのようなものは存在しません。

于 2013-02-05T12:36:58.250 に答える