LINQ query to identify fragments in a series

Suppose I have the following array (my sequences are sorted in ascending order and contain positive integers)

var mySequence = new [] {1, 2, 3, 7, 8, 9, 15, 16, 17}; 

I want to write a linq query to select continuous numbers in a series viewed as a group. So, in the above example, I would get {[1, 2, 3], [7, 8, 9], [15, 16, 17]}.

I could write a sequence of foreach() , pass through each element and see where the sequence takes the jump and get the group there. But is there any LINQ method? I could move my foreach() code to a new extension method, so my code still looks LINQy, but I'm wondering if there is anything in System.Linq for this.

EDIT: I created my own extension (as follows), but Me.Name came up with something very smart in my answer.

 internal class Sequence { public int Start { get; set; } public int End { get; set; } } internal static class EnumerableMixins { public static IEnumerable<Sequence> GroupFragments(this IEnumerable<int> sequence) { if (sequence.Any()) { var lastNumber = sequence.First(); var firstNumber = lastNumber; foreach(var number in sequence.Skip(1)) { if (Math.Abs(number - lastNumber) > 1) { yield return new Sequence() { Start = firstNumber, End = lastNumber }; firstNumber = lastNumber = number; } else { lastNumber = number; } } yield return new Sequence() { Start = firstNumber, End = lastNumber }; } } } 
+5
source share
2 answers

The old trick to find these islands is to subtract the index and numerical value. The result will be unique groups. Using select overload, including index:

 var mySequence = new [] {1, 2, 3, 7, 8, 9, 15, 16, 17}; var groups = mySequence .Select((val,ind) => new{val, group = val - ind}) .GroupBy(v=>v.group, v=>v.val) .Select(v=> v.ToList()).ToList(); 

(Used by ToList here, but of course ToArray can be used if arrays are preferred)

+10
source

I'm a little late, but in any case I want to share this idea. You can also create an iterator as follows:

 public static class Extensions { public static IEnumerable<IEnumerable<int>> ContinuousNumbers(this IEnumerable<int> source) { using (var e = source.GetEnumerator()) { if (e.MoveNext()) { var list = new List<int> { e.Current}; int temp = e.Current; while (e.MoveNext()) { if (e.Current == temp+1) { list.Add(e.Current); temp++; } else { yield return list; list = new List<int> { e.Current}; temp = e.Current; } } if (list.Count > 0) { yield return list; } } } } } 

In another embodiment, the expansion method may be used. Unit :

 var result = mySequence.Aggregate(new List<List<int>>(), (r, current) => { if ( r.Count==0 || (r.Last().Count>0 && r.Last().Last() != current-1)) r.Add(new List<int> { current}); else r.Last().Add(current); return r; }); 
+1
source

Source: https://habr.com/ru/post/1243613/


All Articles