I have an ordered sequence like {1, 3, 5, 6, 8, 9} I want to get first missing element(2 in the example) or max() if sequence contains no missing elements. Now I'm doing it like this:
public static int GetRegisterNumber<T>(this IQueryable<T> enumerable, Func<T, bool> whereFunc, Func<T, int?> selectFunc)
{
var regNums = enumerable.OrderBy(selectFunc).Where(whereFunc).ToArray();
if (regNums.Count() == 0)
{
return 1;
}
for (int i = 0; i < regNums.Count(); i++)
{
if (i + 1 != regNums[i])
{
return regNums[i].Value + 1;
}
}
return regNums.Last().Value + 1;
}
But i think there are much faster methods. Any suggestions?
Step 1: Find the common difference of each pair of consecutive terms in the sequence by subtracting each term from the term that comes directly after it. Step 2: Add the common difference to the number prior to the first missing number in the sequence. Step 3: Repeat Step 2 for any other missing numbers.
Simple Approach So the sum of n elements, that is the sum of numbers from 1 to N can be calculated by using the formula N * (N + 1) / 2. Now find the summation of all elements in the array and subtract it from the summation of first N natural numbers, the value obtained will be the value of the missing element.
Now, sum of natural numbers from 1 to N, can be expressed as Nx(N+1)/2 . In your case N=100. Subtract the sum of the array from Nx(N+1)/2 , where N=100. That is the missing number.
Find the sum of the numbers in the range [1, N] using the formula N * (N+1)/2. Now find the sum of all the elements in the array and subtract it from the sum of the first N natural numbers. This will give the value of the missing element.
Edit: I just noticed that enumerable
is IQueryable<T>
but selectFunc
and whereFunc
are of type Func<T, _>
. This will cause the Enumerable
versions of OrderBy
and Where
to be called, rather than using database calls. You probably want to switch them to Expression<Func<T, _>>
instead.
If you don't want to order regNums
first, here's a O(n) golf-style solution:
var max = regNums.Max(i => (int?)i) ?? 0;
return Enumerable.Range(1, max + 1)
.Except(regNums)
.Min();
By line:
By casting to int?
, Max
will return null
if regNums
is empty, coalesced to 0
.
Build a sequence of all possible registers, including our next value if full.
Subtract the current set of registers.
Pick the lowest.
I'd probably look at something like below; the Where
can be done outside (as can the selector to be honest):
If you expect to start at 1:
public static int GetRegisterNumber<T>(this IEnumerable<T> enumerable,
Func<T, int> selectFunc)
{
int expected = 1;
foreach (T item in enumerable) {
if (selectFunc(item) != expected) return expected;
expected++;
}
return expected;
}
To start with the first item in the list:
public static int GetRegisterNumber<T>(this IEnumerable<T> enumerable,
Func<T, int> selectFunc)
{
bool first = true;
int prev = -1;
foreach (T item in enumerable)
{
int val = selectFunc(item);
if(first) {
prev = val;
first = false;
} else if (val != prev + 1) {
return prev + 1;
}
prev = val;
}
return first ? 1 : prev + 1;
}
It isn't clear how you wanted to handle nulls, so I didn't. Note that this only iterates once, and doesn't buffer everything.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With