Подтвердить что ты не робот

Linq orderby, начните с определенного числа, затем вернитесь к самому низкому

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

Например, для последовательности (1,2,3,4,5,6), если 4 было конкретным числом, порядок будет (4,5,6,1,2,3).

Возможно ли это с помощью linq и С#?

4b9b3361

Ответ 1

List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6 };
int num = 4;
var newList = list.SkipWhile(x=>x!=num)
                    .Concat(list.TakeWhile(x=>x!=num))
                    .ToList();

Ответ 2

int specific = 4;
var numbers = Enumerable.Range(1, 9);

var result = numbers.OrderBy(n => Tuple.Create(n < speficic, n)).ToList();

Я использую здесь небольшой трюк, используя Tuple<bool, int> в качестве компаратора, так как false < true. Альтернативой является:

var result = numbers.OrderBy(n => n < speficic).ThenBy(n => n).ToList();

ИЗМЕНИТЬ после теста, я обнаружил, что второе решение .OrderBy .ThenBy намного быстрее, чем решение Tuple. Я считаю, потому что FCL использует Comparer<T>.Default в качестве компаратора, который затрачивает время на построение.

Ответ 3

OrderBy() достаточно мощный сам по себе, и для расширения его сферы там ThenBy(), поэтому, на мой взгляд, более чистый способ сделать это:

var list = new[] {1, 2, 3, 4, 5, 6};
var pivot = 4;
var order = list.OrderBy(x => x == pivot ? 0 : 1).ThenBy(y => y < pivot ? 1: 0);

Ответ 4

Вы можете реализовать пользовательский IComparer.

Что-то вроде следующего (код примечания не проверен!):

List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6 };
list.OrderBy(n => n, new IntComparer(4));

public class IntComparer : IComparer<int>
{

    int start; 

    public IntComparer (int start)
    {
        this.start = start;
    }

    // Compares by Height, Length, and Width. 
    public int Compare(int x, int y)
    {
        if (x >= start && y < start)
            // X is greater than Y
            return 1;
        else if (x < start && y >= start)
            // Y is greater than X
            return -1;
        else if (x == y)
            return 0;
        else 
            return x > y ? 1 : -1;
    }
} 

Ответ 5

 List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6 };
 item = 4;

 var index = input.IndexOf(item);
 var firstList = input.Take(index);

 return input.Except(firstList)
             .Concat(firstList)
             .ToList();

Ответ 6

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

public class StartWithComparer<T> : IComparer<T>
{
    private T startWith;
    private IComparer<T> baseComparer = Comparer<T>.Default;
    public StartWithComparer(T startWith, IComparer<T> baseComparer = null)
    {
        this.startWith = startWith;
        if (baseComparer != null) this.baseComparer = baseComparer;
    }

    public int Compare(T x, T y)
    {
        int xToS = baseComparer.Compare(x, startWith);
        int yToS = baseComparer.Compare(y, startWith);

        if (xToS >= 0 && yToS < 0)
            return -1;
        else if (xToS < 0 && yToS >= 0)
            return 1;
        else
            return baseComparer.Compare(x, y);
    }
}

Вызывается

new[] { 1, 2, 3, 4, 5, 6 }.OrderBy(i => i, new StartWithComparer<int>(4))

Ответ 7

Вы можете использовать (или злоупотреблять, я допускаю) простое вычитание, чтобы выполнить это:

var seq = Enumerable.Range(0, 10);
int n = 4;
int m = seq.Max() + 1; // or a magic number like 1000, thanks RB.

var ordered = seq.OrderBy(x => x >= n ? x - m : x);

foreach(int i in ordered)
    Console.WriteLine(i);

Кроме того, если числа становятся больше, помните о переполнении целых чисел. Для простых случаев это может быть хорошо, хотя.

Вот лучшее решение (вдохновленное другими ответами):

var seq = Enumerable.Range(0, 10);
int n = 4;

var ordered = seq.Where(x => x >= n).OrderBy(x => x)
    .Concat(seq.Where(x => x < n).OrderBy(x => x));

foreach(int i in ordered)
    Console.WriteLine(i);

Сортирует каждую последовательность. прежде чем конкатенировать их. T_12 спросил в комментарии, сортируются ли они по возрастанию. Если да, перейдите с L.B. решение вместо моего, так как OrderBy ударяет по меньшей мере O(n log n) вместо O(n) (linear).

Ответ 8

        List<int> list = new List<int>()
        {
            1,2,3,4,5,6
        };
        int number = 4;
        int max = list.Max();
        var result = list.OrderBy(i => i >= number ? i : max + i);

Ответ 9

Я предлагаю здесь еретическое решение, потому что оно не использует стандартные операторы LINQ:

IEnumerable<int> GetSequence(IList<int> input, int index) {
 for (var i = index; i < input.Count; i++) yield return input[i];
 for (var i = 0; i < index; i++) yield return input[i];
}

Я думаю, что это ясно показывает намерение.

Я не думаю, что странные искажения, которые вы должны выполнять со стандартными операторами запросов LINQ (комбо Skip, Take, Concat), являются читабельными или поддерживаемыми. Я думаю, что это было бы злоупотреблением, чтобы использовать их в этом случае только ради этого. Петли в порядке.

Ответ 10

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

public static IEnumerable<T> Shift<T>(this IEnumerable<T> subject, T shouldBeFirst)
{
    return subject.Shift(shouldBeFirst, EqualityComparer<T>.Default);
}
public static IEnumerable<T> Shift<T>(this IEnumerable<T> subject, T shouldBeFirst, IEqualityComparer<T> comparer)
{
    var found = false;
    var queue = new Queue<T>();
    foreach (var item in subject)
    {
        if(!found)
            found = comparer.Equals(item, shouldBeFirst);

        if(found)
            yield return item;
        else
            queue.Enqueue(item);
    }
    while(queue.Count > 0)
        yield return queue.Dequeue();
}

Использование

var list = new List<int>() { 1, 2, 3, 4, 5, 6 };
foreach (var i in list.Shift(4))
    Console.WriteLine(i);

Печать

4
5
6
1
2
3

Ответ 11

Если ваши данные List<T>, это работает:

var sequence = new[] { 1, 2, 3, 4, 5, 6 }.ToList();
List<int> result;
int start = 4;
int index = sequence.IndexOf(start);
if (index == 0)
    result = sequence;
else if (index > -1)
{
    result = sequence.GetRange(index, sequence.Count - index);
    var secondPart = sequence.GetRange(0, sequence.Count - index);
    result.AddRange(secondPart);
}

Это не упорядочивает, а создает новый список.