2

ObservableCollectionソートしたいものがありますが、その場所ではありませんが、ソートされた新しいコピーを作成したいと考えています。

気の利いたラムダ式または LINQ を使用してリストを並べ替える方法の例はたくさんありますが、並べ替えたいフィールドをコードにハードコードすることはできません。

NSSortDescription私はのような作品の配列を持っていSortDescriptionます。プロパティのstring名前には がありますが、方向はbool( true= 昇順) で指定されます。配列の最初の値はプライマリ ソート フィールドにする必要があり、そのフィールドの値が一致する場合は、2 番目のソート記述子を使用する必要があります。

例:

Artist: Bob Marley, Title: No Woman No Cry
Artist: Bob Marley, Title: Could You Be Loved
Artist: Infected Mushroom, Title: Converting Vegetarians
Artist: Bob Marley, Title: One Love
Artist: Chemical Brothers, Title: Do It Again

ソート記述子: アーティストの降順、タイトルの昇順。

結果:

Artist: Infected Mushroom, Title: Converting Vegetarians
Artist: Chemical Brothers, Title: Do It Again
Artist: Bob Marley, Title: Could You Be Loved
Artist: Bob Marley, Title: No Woman No Cry
Artist: Bob Marley, Title: One Love

これを達成する方法について何か提案はありますか?

4

4 に答える 4

1

ソースが既に注文されているかどうかに応じて、基本的にOrderByまたはのいずれかの効果を持つ次の拡張メソッドを書いたことがあります。ThenBy

public static class Extensions {
    public static IOrderedEnumerable<TSource> OrderByPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer, bool descending) {
        var orderedSource = source as IOrderedEnumerable<TSource>;
        if (orderedSource != null) {
            return orderedSource.CreateOrderedEnumerable(keySelector, comparer, descending);
        }
        if (descending) {
            return source.OrderByDescending(keySelector, comparer);
        }
        return source.OrderBy(keySelector, comparer);
    }

    public static IOrderedEnumerable<TSource> OrderByPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
        return source.OrderByPreserve(keySelector, null, false);
    }

    public static IOrderedEnumerable<TSource> OrderByPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) {
        return source.OrderByPreserve(keySelector, comparer, false);
    }

    public static IOrderedEnumerable<TSource> OrderByDescendingPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
        return source.OrderByPreserve(keySelector, null, true);
    }

    public static IOrderedEnumerable<TSource> OrderByDescendingPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) {
        return source.OrderByPreserve(keySelector, comparer, true);
    }
}

OrderByインターフェイスは/と同じです (または、ブール値としてOrderByDescending渡すこともできます)。descendingあなたは書ける:

list.OrderByPreserve(x => x.A).OrderByPreserve(x => x.B)

これは次と同じ効果があります:

list.OrderBy(x => x.A).ThenBy(x => x.B)

したがって、プロパティ名の任意のリストを使用して、 keyboardPのソリューションを簡単に使用できます。

public static IEnumerable<TSource> OrderByProperties<TSource>(IEnumerable<TSource> source, IEnumerable<string> propertyNames) {
    IEnumerable<TSource> result = source;
    foreach (var propertyName in propertyNames) {
        var localPropertyName = propertyName;
        result = result.OrderByPreserve(x => x.GetType().GetProperty(localPropertyName).GetValue(x, null));
    }
    return result;
}

(localPropertyName変数は、クエリが実行されるまでに反復変数が変更されるため、ここで使用されます。詳細については、この質問を参照してください)


これに関して考えられる問題は、リフレクション操作がアイテムごとに実行されることです。効率的に呼び出すことができるように、各プロパティの LINQ 式を事前に作成しておくことをお勧めします (このコードにはSystem.Linq.Expressions名前空間が必要です)。

public static IEnumerable<TSource> OrderByProperties<TSource>(IEnumerable<TSource> source, IEnumerable<string> propertyNames) {
    IEnumerable<TSource> result = source;
    var sourceType = typeof(TSource);
    foreach (var propertyName in propertyNames) {
        var parameterExpression = Expression.Parameter(sourceType, "x");
        var propertyExpression = Expression.Property(parameterExpression, propertyName);
        var castExpression = Expression.Convert(propertyExpression, typeof(object));
        var lambdaExpression = Expression.Lambda<Func<TSource, object>>(castExpression, new[] { parameterExpression });
        var keySelector = lambdaExpression.Compile();
        result = result.OrderByPreserve(keySelector);
    }
    return result;
}

基本的に、これらのExpression行が行っているのは式x => (object)x.A(「A」は現在のプロパティ名) を作成することであり、これは順序付けキー セレクターとして使用されます。

使用例は次のとおりです。

var propertyNames = new List<string>() { "Title", "Artist" };
var sortedList = OrderByProperties(list, propertyNames).ToList();

昇順/降順のロジックを追加するだけです。

于 2013-07-30T23:23:44.773 に答える