20

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

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

linq & c# 有可能吗?

4

11 回答 11

28
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();
于 2012-09-25T09:09:14.907 回答
11
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。我相信这是因为 FCLComparer<T>.Default用作比较器,这需要花费时间来构建。

于 2012-09-25T09:15:05.530 回答
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);
于 2012-09-25T09:33:43.413 回答
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;
    }
} 
于 2012-09-25T09:14:09.630 回答
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();
于 2012-09-25T09:16:31.447 回答
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))
于 2012-09-25T09:25:22.890 回答
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 在评论中询问它们是否按升序排序。如果是这样,请使用LB 的解决方案而不是我的解决方案,因为这OrderBy至少会打击努力O(n log n)而不是O(n)(线性)。

于 2012-09-25T09:08:53.037 回答
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);
于 2012-09-25T09:09:38.807 回答
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 的组合)执行的奇怪扭曲是可读或可维护的。我认为在这种情况下仅仅为了它而使用它们是一种滥用。循环很好。

于 2012-09-25T09:14:23.063 回答
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
于 2012-09-25T16:53:27.097 回答
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);
}

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

于 2012-09-25T09:17:11.303 回答