获得使用LINQ一个有效整(get next available integer using LIN

2019-07-30 00:56发布

说我有一个整数列表:

List<int> myInts = new List<int>() {1,2,3,5,8,13,21};

我想获得下一个可用的整数,通过增加整数排序。 不是最后或最高的国家之一,但在这种情况下,下一个整数,是不是在这个列表中。 在这种情况下的数量是4。

是否有一个LINQ声明说会给我这个? 如:

var nextAvailable = myInts.SomeCoolLinqMethod();

编辑:废话。 我说,答案应该是2,但我的意思是4。我对此表示道歉!

例如:假设你是负责分发进程ID。 你想得到当前进程ID列表,并发出下一个,但未来不应该只是为最高的值加一。 相反,它应该可以从进程ID的有序列表中的下一个。 你可以得到下一个可用的开始是最高的,它其实并不重要。

Answer 1:

public static class IntExtensions
{
    public static int? SomeCoolLinqMethod(this IEnumerable<int> ints)
    {
        int counter = ints.Count() > 0 ? ints.First() : -1;

        while (counter < int.MaxValue)
        {
            if (!ints.Contains(++counter)) return counter;
        }

        return null;
    }
}

用法:

var nextAvailable = myInts.SomeCoolLinqMethod();


Answer 2:

我看到很多是编写自定义扩展方法的答案,但可以与标准的LINQ扩展方法和静态解决这个问题可枚举类:

List<int> myInts = new List<int>() {1,2,3,5,8,13,21};

// This will set firstAvailable to 4.
int firstAvailable = Enumerable.Range(1, Int32.MaxValue).Except(myInts).First();


Answer 3:

通过@Kevin提供的答案有一个不良的性能配置。 该逻辑将访问源序列多次:一次用于.Count呼叫,一旦为.FirstOrDefault呼叫,并且每进行一次.Contains呼叫。 如果IEnumerable<int>实例是一个延迟序列,诸如一个的结果.Select呼叫,这将导致至少2点计算该序列的,具有一次为每个号码。 即使你传递一个列表的方法,这将有可能经历的每个检查到的数量整个列表。 试想一下,在序列运行它{ 1, 1000000 } ,你可以看到它不会表现良好。

LINQ致力于迭代源序列不超过一次。 这是一般的可能,并可以对你的代码的性能有很大的影响。 下面是一个扩展方法,会遍历序列一次。 它通过寻找每个连续对之间的差,然后添加1到第一下数是大于1远离下一个编号:

public static int? FirstMissing(this IEnumerable<int> numbers)
{
    int? priorNumber = null;

    foreach(var number in numbers.OrderBy(n => n))
    {
        var difference = number - priorNumber;

        if(difference != null && difference > 1)
        {
            return priorNumber + 1;
        }

        priorNumber = number;
    }

    return priorNumber == null ? (int?) null : priorNumber + 1;
}

由于这种扩展方法可以在整数的任意序列被调用,我们要确保对它们进行排序,我们遍历之前。 然后,我们计算出当前号码和先前数之间的差异。 如果这是在列表中的第一个数字, priorNumber将是无效,因此difference将是无效的。 如果这不是在列表中的第一个数字,我们检查,看看是否与现有数之差正好是1,如果没有,我们知道还有一定的差距,我们可以添加1现有数量。

您可以调整return语句来处理序列0或1个项目,您看合适; 我选择用于序列空序列和n + 1返回null { n }



Answer 4:

这将是相当有效:

static int Next(this IEnumerable<int> source)
{
    int? last = null;
    foreach (var next in source.OrderBy(_ => _))
    {
        if (last.HasValue && last.Value + 1 != next)
        {
            return last.Value + 1;
        }

        last = next;
    }

    return last.HasValue ? last.Value + 1 : Int32.MaxValue;
}


Answer 5:

好吧,这里是我想出了对我的作品的解决方案。

var nextAvailableInteger = Enumerable.Range(myInts.Min(),myInts.Max()).FirstOrDefault( r=> !myInts.Contains(r));

如果任何人有一个更优雅的解决方案,我会很乐意接受的。 但现在,这就是我把我的代码和移动上。

编辑:这是我凯文的建议添加一个扩展方法后实施。 这是真正的答案 - 没有任何一个LINQ的扩展会做这样更有意义加我自己。 这真的是我一直在寻找。

public static int NextAvailableInteger(this IEnumerable<int> ints)
{
    return NextAvailableInteger(ints, 1); // by default we use one
}
public static int NextAvailableInteger(this IEnumerable<int> ints, int defaultValue)
{
    if (ints == null || ints.Count() == 0) return defaultValue;

    var ordered = ints.OrderBy(v => v);
    int counter = ints.Min();
    int max = ints.Max();

    while (counter < max)
    {
        if (!ordered.Contains(++counter)) return counter;
    }
    return (++counter);
}


Answer 6:

不知道这是否有资格作为一个很酷的LINQ的方法,但使用左外连接的想法从这个SO回答

var thelist = new List<int> {1,2,3,4,5,100,101};

var nextAvailable = (from curr in thelist
                    join next in thelist
                        on curr + 1 equals next into g
                    from newlist in g.DefaultIfEmpty()
                    where !g.Any ()
                    orderby curr
                    select curr + 1).First();

这使处理上的SQL Server端,如果你使用LINQ to SQL中,并允许您不必从服务器到存储拉动ID列表。



文章来源: get next available integer using LINQ