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

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

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

这对linq&c#有可能吗?

List list = new List() { 1, 2, 3, 4, 5, 6 }; int num = 4; var newList = list.SkipWhile(x=>x!=num) .Concat(list.TakeWhile(x=>x!=num)) .ToList(); 
 int specific = 4; var numbers = Enumerable.Range(1, 9); var result = numbers.OrderBy(n => Tuple.Create(n < speficic, n)).ToList(); 

我在这里使用了一个小技巧,使用Tuple作为比较器,因为false < true 。 另一种选择是:

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

在基准测试后编辑我找到了第二个解决方案.OrderBy .ThenByTuple解决方案快得多。 我相信这是因为FCL使用Comparer.Default作为比较器,这需要花费时间来构建。

OrderBy()本身非常强大,而且为了扩展它的范围,还有ThenBy()所以,在我看来,更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); 

您可以实现自定义IComparer 。

类似下面的内容(注意代码未经过测试!):

 List list = new List() { 1, 2, 3, 4, 5, 6 }; list.OrderBy(n => n, new IntComparer(4)); public class IntComparer : IComparer { 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; } } 
  List list = new List() { 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(); 

对于一般情况,以下是对于任何类应该为此定制的IComparer

 public class StartWithComparer : IComparer { private T startWith; private IComparer baseComparer = Comparer.Default; public StartWithComparer(T startWith, IComparer 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(4)) 
  List list = new List() { 1,2,3,4,5,6 }; int number = 4; int max = list.Max(); var result = list.OrderBy(i => i >= number ? i : max + i); 

我会在这里提出一个异端解决方案,因为它根本没有使用标准的LINQ运算符:

 IEnumerable GetSequence(IList 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的组合)执行的奇怪扭曲是可读或可维护的。 我认为仅仅是为了它,在这种情况下使用它们是一种滥用。 循环很好。

你可以使用(或滥用,我承认)一个简单的减法来完成这个:

 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在评论中询问他们是否按升序排序。 如果是这样的话,请使用LB的解决方案而不是我的解决方案 ,因为OrderBy将努力至少O(n log n)而不是O(n) (线性)。

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

 public static IEnumerable Shift(this IEnumerable subject, T shouldBeFirst) { return subject.Shift(shouldBeFirst, EqualityComparer.Default); } public static IEnumerable Shift(this IEnumerable subject, T shouldBeFirst, IEqualityComparer comparer) { var found = false; var queue = new Queue(); 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() { 1, 2, 3, 4, 5, 6 }; foreach (var i in list.Shift(4)) Console.WriteLine(i); 

打印

 4 5 6 1 2 3 

如果您的数据是List则可以:

 var sequence = new[] { 1, 2, 3, 4, 5, 6 }.ToList(); List 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); } 

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