【问题标题】:Linq orderby, start with specific number, then return to lowestlinq orderby,从特定数字开始,然后返回到最低
【发布时间】:2012-09-16 18:29:19
【问题描述】:

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

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

linq 和 c# 有可能吗?

【问题讨论】:

  • 你的序列总是升序排列的?
  • 我错过了它是一个数字并返回的事实,我有阅读障碍(正如我的描述所说)我把它读成降序
  • 您可以使用模运算符 (a % b) 做一些魔术来计算您的索引,在您的情况下,它将如下所示:i = ((i + 2) % 6) + 1跨度>
  • 在您的示例中,4 是开始的索引,还是开始的元素?在示例序列中,两者重合。 -- 糟糕,我看错了 - 实际上 4 的索引为 3。没关系。

标签: c# linq


【解决方案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();

【讨论】:

  • +1 比我的要优雅得多,因为数字总是按升序排列。
  • @Mudu 你当然可以将数字升序作为第一步:)
  • @RB 是正确的.. 使用此算法,您需要先升序排序.. 因为如果您有一个类似 {3,1,2,4,6,5}.. 的列表,输出将是 {4,6,5,3,1,2}
【解决方案2】:
int specific = 4;
var numbers = Enumerable.Range(1, 9);

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

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

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

EDIT 在基准测试后我发现第二个解决方案 .OrderBy .ThenByTuple 解决方案快得多。我相信是因为FCL使用Comparer&lt;T&gt;.Default作为比较器,构建起来比较费时间。

【讨论】:

  • @KSaini:Tuple 实现了IComparable,其中使用默认比较器按顺序比较 T1 和 T2。就像它在msdn中的定义一样简单。
【解决方案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;
        }
    } 
    

    【讨论】:

    • 如果x == starty &lt; start,当它应该是y &gt; x 时,这将显示y &lt; x
    • @Rawling 谢谢。正如我所说,代码没有经过测试,因此可能存在边缘情况(单元测试会捕获;))。我已更新代码以使用 &gt;= 开始检查。
    • 我想你现在已经搞定了 :)
    • 实际上我认为你的1-1(在if 和第一个else if)是错误的。但除此之外,我非常喜欢你的逻辑,以至于我偷了它。哇哈哈。
    【解决方案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,对于 any 类应该是这样的。

      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】:
              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】:

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

        【讨论】:

        • LB发布的查询翻译为“取所有大于n的数字,然后连接所有小于n的数字”。这肯定是表达程序员意图的一种非常直接的方式吗?
        【解决方案9】:

        您可以使用(或滥用,我承认)一个简单的减法来完成此操作:

        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)(线性)。

        【讨论】:

        • 您可以使用seq.Max() + 1,而不是使用幻数1000。当然,您仍然需要担心整数溢出。
        【解决方案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&lt;T&gt;,则此方法有效:

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

          这并不是真正的排序,而是创建一个新列表。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多