Linq orderby,从具体数字开始,然后返回最低

时间:2012-09-25 09:03:09

标签: c# linq

我有一组数据,我想从特定的数字开始重新订购,然后,当达到最高数字时,返回最低数据然后继续递增。

例如,对于序列(1,2,3,4,5,6),如果4是特定数字,则顺序将变为(4,5,6,1,2,3)。

这可能与linq& C#?

11 个答案:

答案 0 :(得分:26)

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();

答案 1 :(得分:10)

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 .ThenByTuple解决方案快得多。我相信这是因为FCL使用Comparer<T>.Default作为比较器,这需要花费时间来构建。

答案 2 :(得分:4)

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

答案 3 :(得分:3)

您可以实施自定义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;
    }
} 

答案 4 :(得分:2)

 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();

答案 5 :(得分:2)

对于一般情况,以下是任何类的自定义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))

答案 6 :(得分:1)

您可以使用(或滥用,我承认)一个简单的减法来实现这一目标:

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.'s solution而不是我的,因为OrderBy将努力至少O(n log n)而不是O(n)(线性)。

答案 7 :(得分:1)

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

答案 8 :(得分:1)

我会在这里提出一个异端解决方案,因为它根本没有使用标准的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的组合)执行的奇怪扭曲是可读或可维护的。我认为仅仅是为了它而在这种情况下使用它们是一种滥用。循环很好。

答案 9 :(得分:1)

将序列移动到给定项目开始的扩展方法。这也只会通过原始序列一次,这可能重要也可能不重要。这也假设序列已经按照你想要的方式排序,除了移位。

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

答案 10 :(得分:0)

如果您的数据是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);
}

这不是真正订购,而是创建一个新列表。