Reputation: 21
I have two methods in an IntExtensions class to help generate the next available incremental value (which is not in a list of existing integers which need to be excluded). I dont think I'm addressing the NextIncrementalValueNotInList method in the best way and am wondering if I can better use linq to return the next available int?
public static bool IsInList(this int value, List<int> ListOfIntegers) {
if (ListOfIntegers.Contains(value))
return true;
return false;
}
public static int NextIncrementalValueNotInList(this int value,
List<int> ListOfIntegers) {
int maxResult;
maxResult = ListOfIntegers.Max() + 1;
for (int i = value; i <= maxResult; i++)
{
if (!(i.IsInList(ListOfIntegers)))
{
return i;
}
}
return maxResult;
}
Upvotes: 2
Views: 329
Reputation: 3333
Using linq your method will look like:
return IEnumerable.Range(1, ListOfIntegers.Count + 1)
.Except(ListOfIntegers)
.First();
Upvotes: 5
Reputation: 42333
You don't actually need to calculate the Max
value - just keep incrementing i
until you find a value that doesn't exist in the list, e.g:
public static int NextIncrementalValueNotInList(this int value,
List<int> ListOfIntegers)
{
int i = value;
while(true)
{
if (!(i.IsInList(ListOfIntegers)))
{
return i;
}
i++;
}
return maxResult;
}
. Besides that, I'm not sure if there's much more you can do about this unless:
ListOfIntegers
is guaranteed to be, or needs to be, sorted, orListOfIntegers
doesn't actually need to be a List<int>
If the answer to the first is no, and to the second is yes, then you might instead use a HashSet<int>
, which might provide a faster implementation by allowing you to simply use HashSet<T>
's own bool Contains(T)
method:
public static int NextIncrementalValueNotInList(this int value,
HashSet<int> ListOfIntegers)
{
int i = value;
while(true)
{
if (!(ListOfIntegers.Contains(i))
{
return value;
}
i++;
}
}
Note that this version shows how to do away with the Max
check also.
Although be careful of premature optimisation - if your current implementation is fast enough, then I wouldn't worry. You should properly benchmark any alternative solution with extreme cases as well as real-world cases to see if there's actually any difference.
Also what you don't want to do is use my suggestion above by turning your list into a HashSet
for every call. I'm suggesting changing entirely your use of List
to HashSet
- any piecemeal conversion per-call will negate any potential performance benefits due to the overhead of creating the HashSet
.
Finally, if you're not actually expecting much fragmentation in your integer list, then it's possible that a HashSet
might not be much different from the current Linq version, because it's possibly going to end up doing similar amounts of work anyway.
Upvotes: 2
Reputation: 6692
I guess it starting at 1.
You could also proceed like this:
IEnumerable.Range(1, ListOfIntegers.Count)
.Where(i => !ListOfIntegers.Contains(i))
.Union(new []{ ListOfIntegers.Count + 1 })
.First();
Upvotes: 2