Выборочный заказ с LINQ, частичный заказ - PullRequest
5 голосов
/ 17 февраля 2010

Допустим, у меня есть список объектов:

var items = new {
    new { Order = 0 },
    new { Order = 1 },
    new { Order = -1 },
    new { Order = 3 },
    new { Order = 2 },
    new { Order = -1 }
};

Мне нужно сделать так, чтобы элементы с Order > -1 были в верхней части списка, упорядоченного по возрастанию, а остальные элементы с Order == -1 следовали за ними.

Есть ли более элегантный способ сделать это, чем использовать предложения Conact() и Where():

var orderedItems = items.Where(x => x.Order > -1).OrderBy(x => x.Order)
                   .Conact(items.Where(x => x.Order == -1);

Чтобы после сортировки этот список выглядел так:

var items = new {
    new { Order = 0 },
    new { Order = 1 },
    new { Order = 2 },
    new { Order = 3 },
    new { Order = -1 },
    new { Order = -1 }
};

Также items список в реальном сценарии уже является сложным IQueryable<T> объектом. Вот почему я пытаюсь найти наиболее оптимальный способ сделать такой выборочный порядок.

Ответы [ 6 ]

7 голосов
/ 17 февраля 2010

Вы можете попробовать это - вы получите ожидаемый результат:

items.OrderBy(x.Order => x.Order == -1).ThenBy(x.Order => x.Order);
6 голосов
/ 17 февраля 2010

Как упоминал Майк, в вашем примере это будет работать автоматически, но, скажем, мы хотели сначала получить все элементы -1, а затем отсортировать оставшиеся элементы в порядке убывания. Это можно сделать, используя хороший трюк. Вы можете использовать несколько ключей при заказе элементов. Первый ключ может быть логическим значением, которое будет false для всех значений -1 (поэтому они будут первыми) и true для всех других значений (поэтому они не будут переупорядочены). Второй ключ может быть любым, что вы хотите заказать остальные элементы. Например:

var nums = new int[] { -1, 4, 2, 3, -1, 4, 7 };
var q = from n in nums
        orderby n != -1, n descending
        select n;

Сначала будут получены все значения, для которых n != -1 равно false, а затем все элементы упорядочены с использованием n descending, поэтому вы получите:

-1, -1, 7, 4, 4, 3, 2

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

3 голосов
/ 17 февраля 2010

Если вы заказываете по возрастанию, -1 уже должен быть в верхней части списка, потому что это наименьшее значение.

Однако в более общем плане, если вы все еще хотите применить другую сортировку к подмножествам данных, я не думаю, что будет более элегантный способ, потому что это именно то, что вы делаете, и объединение является логически точным. Вы пытаетесь вытащить два отдельных подмножества данных, отсортировать их по-разному, а затем объединить их вместе, что является одной из вещей, для которых я буду использовать объединение.

1 голос
/ 17 февраля 2010

Также здесь есть собственный компаратор, если вы хотите, чтобы -1 отображался первым, а остальные - по убыванию , но почему-то я нахожу его более элегантным :) Обратите внимание, что он предназначен для целых чисел

class Comparer : IComparer<int>
{
  public int Compare(int x, int y)
  {
    if (x == -1 || y == -1) return x - y;
    return y - x;
  }
}
1 голос
/ 17 февраля 2010
OrderBy(x => x.Order < 0 ? int.MaxValue : x.Order)

или если вам нужно упорядочить отрицательные значения в порядке убывания

OrderBy(x => x.Order < 0 ? (long)int.MaxValue - x.Order : (long)x.Order)
0 голосов
/ 17 февраля 2010

Используйте пользовательский компаратор , чтобы определить желаемый порядок.

public class MyComparer : IComparer<int>
{
    public int Compare(int a, int b)
    {
        if ((a < 0) && (b >= 0))
        {
            return 1;
        }
        if ((a >= 0) && (b < 0))
        {
            return -1;
        }
        return int.Compare(a, b);
    }
}

Тогда вы можете сделать:

var orderedItems = items.OrderBy(x => x.Order, new MyComparer());
...