DEC 202013
I recently had a need to determine if a sequence of integers was solid, not broken, and whether it contained a gap of any missing numbers to report to the end user. I researched the issue and was dismayed that the examples found on the internet seemed to have unnecessary overheads of copied arrays or other artifacts and were too cumbersome for my needs. From that I wrote these C# extensions which work for any .Net above 3.5. Below I document the extension methods to get the missing numbers of a sequence, quickly determine if a sequence is broken and finally report where the existing numbers before the break of the sequence. See the end for the whole extension class for easier copying.
Problem Definition
If one has a set of numbers say
{ 2, 4, 7, 9 }
it has a broken sequence because there are missing numbers from the set 2-9. Those missing numbers are:
{ 3, 5, 6, 8 }
Find Missing Numbers In a Sequence
This method is the one I created first. It uses the Linq Aggregate extension to enumerate over the numbers in the set. If there is a gap which is greater than 1 between the numbers (the difference below is > 0 but same concept) then it reports the numbers missing between the two.
public static IEnumerable< int > SequenceFindMissings( this IList< int > sequence) { var missing = new List< int >(); if ((sequence != null ) && (sequence.Any())) { sequence.Aggregate((seed, aggr) => { var diff = (aggr - seed) - 1; if (diff > 0) missing.AddRange(Enumerable.Range((aggr - diff), diff)); return aggr; }); } return missing; } |
Quickly Determine Broken Sequence
Is the sequence broken from the first number to the last in the set?
public static bool IsSequenceBroken( this IEnumerable< int > sequence) { bool broken = false ; if (sequence != null ) { var sequenceAsList = sequence.ToList(); if (sequenceAsList.Any()) { int lastValue = sequence.First(); broken = sequence.Any(value => { if ((value - lastValue) > 1) return true ; lastValue = value; return false ; }); } } return broken; } |
Report Last Valid Number Before The Break
This is useful in situations where one needs to report where the break happens, say the user is editing in a grid and one highlights the existing number which precedesthe missing number(s).
Example here returns a 2 and 5 which are the numbers which precede the break.
( new List() { 1, 2, 4, 5, 7, 8}).SequenceReportMissingsBreakStarts() |
Here is the method:
public static IEnumerable< int > SequenceReportMissingsBreakStarts( this IList< int > sequence) { var breaks = new List< int >(); if ((sequence != null ) && (sequence.Any())) { sequence.Aggregate((seed, aggr) => { var diff = (aggr - seed) - 1; if (diff > 0) breaks.Add(seed); return aggr; }); } return breaks; } |
Full Extension Source With Comments
Here is the code for an easier copy
public static class SequenceExtensions { /// /// Take a sequence of numbers and if there are any gaps greater than 1 between the numbers, /// report true. /// |
/// A set of numbers to check.
/// True if the there is a break in the sequence of numbers.
public
static
bool
IsSequenceBroken(
this
IEnumerable<
int
> sequence)
{
bool
broken =
false
;
if
(sequence !=
null
)
{
var sequenceAsList = sequence.ToList();
if
(sequenceAsList.Any())
{
int
lastValue = sequence.First();
broken = sequence.Any(value =>
{
if
((value - lastValue) > 1)
return
true
;
lastValue = value;
return
false
;
});
}
}
return
broken;
}
///
/// Take a sequence of numbers and report the missing numbers. Stop at first break found.
///
/// Set of Numbers
/// True of sequence has missing numbers
public
static
IEnumerable<
int
> SequenceFindMissings(
this
IList<
int
> sequence)
{
var missing =
new
List<
int
>();
if
((sequence !=
null
) && (sequence.Any()))
{
sequence.Aggregate((seed, aggr) =>
{
var diff = (aggr - seed) - 1;
if
(diff > 0)
missing.AddRange(Enumerable.Range((aggr - diff), diff));
return
aggr;
});
}
return
missing;
}
///
/// A missing break start in a sequence is where the drop off occurs in the sequence.
/// For example 3, 5, has a missing break start of the #3 for #4 is the missing.
///
/// Set of Numbers
/// The list of break numbers which exist before the missing numbers.
public
static
IEnumerable<
int
> SequenceReportMissingsBreakStarts(
this
IList<
int
> sequence)
{
var breaks =
new
List<
int
>();
if
((sequence !=
null
) && (sequence.Any()))
{
sequence.Aggregate((seed, aggr) =>
{
var diff = (aggr - seed) - 1;
if
(diff > 0)
breaks.Add(seed);
return
aggr;
});
}
return
breaks;
}
}
Hope this helps!
No comments:
Post a Comment