Применить произвольное количество OrderBy () в LINQ - PullRequest
1 голос
/ 25 октября 2011

Я написал метод расширения, который может принимать произвольное число или OrderBy () и применять их рекурсивно ....

Вероятно, лучше показать пример:

public static IEnumerable<User> ApplyOrdering(this IEnumerable<User> users,
                                              int index,
                                              params Func<User, string>[] sorts)
{
    if(index == sorts.Length)
        return users;

    users = users.OrderBy(sorts[index]);
    index++;
    return users.ApplyOrdering(index, sorts);
}

// And it's called like this:
void Example()
{
    var users = GetUnsortedUsers();

    var sortedUsers = users.ApplyOrdering(0, u => u.DisplayName,
                                             u => u.Username,
                                             u => u.Email);
}

Не так много вопросов, но я хочу знать, можно ли это сделать лучше и элегантнее?

С одной стороны, мне не нравится объявлять начальный индекс ввызов метода.Другой возможный придирка - это вызов .ApplyOrdering () в последний раз, чтобы просто вернуть пользователей.

Что ты думаешь ?!Я хотел бы увидеть некоторые из умных решений, которые вы, гуру C #, придумали!

РЕДАКТИРОВАТЬ: Я знаю, что некоторые из вас укажут, что лучше и выразительнее просто писать запросы, такие какэто:

var orderedUsers = users.OrderBy(u => u.DisplayName)
                        .ThenBy(u => u.Username)
                        .ThenBy(u => u.Email);

Но в моем сценарии количество и тип фильтров не всегда одинаковы, поэтому мне нужно было универсальное решение, которое могло бы вместить любое количество фильтров.

Ответы [ 2 ]

6 голосов
/ 25 октября 2011

В настоящее время он сломан - он будет эффективно применять порядок в обратном направлении . Я подозреваю, что вы хотите:

// Slightly more generic, although it's still requiring projections to string...
public static IEnumerable<T> ApplyOrdering<T>(this IEnumerable<T> source,
                                              params Func<T, string>[] sorts)
{
    // TODO: Argument validation
    if (sorts.Length == 0)
    {
        return source;
    }

    IOrderedEnumerable<T> ordered = source.OrderBy(sorts[0]);
    foreach (var ordering in sorts.Skip(1))
    {
        ordered = ordered.ThenBy(ordering);
    }
    return ordered;
}
2 голосов
/ 25 октября 2011

Ваш код не работает, ваш список будет отсортирован только по последнему указанному вами OrderBy.

Вы должны использовать OrderBy для первого и ThenBy для остальных.

Вот что я бы сделал

public static IEnumerable<User> ApplyOrdering(this IEnumerable<User> users, params Func<User, string>[] sorts) 
{     
    var sorted = users.OrderBy(sorts[0]);
    for(int i = 1; i < sorts.length; i++)
    {
        sorted = sorted.ThenBy(sorts[i]);
    }
    return sorted; 
} 

Я не потратил время на тестирование своего кода, и я уверен, что есть лучшее решение.

...