2016-02-22 14 views
5

Supponiamo che io ho il seguente array (le mie sequenze sono tutti ordinati in ordine crescente, e contenere interi positivi)LINQ Query per identificare i frammenti di una serie

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

Voglio scrivere una query LINQ per selezionare il continuo numeri di una serie trattati come un gruppo. Quindi, nell'esempio sopra otterrei {[1, 2, 3], [7, 8, 9], [15, 16, 17]}.

Potrei scrivere una sequenza foreach(), scorrere attraverso ogni elemento e vedere dove la sequenza sta prendendo un salto e produrre un gruppo lì. Ma c'è qualche modo LINQ di farlo? Potrei essere in grado di spostare il mio codice foreach() in un nuovo metodo di estensione, quindi il mio codice sembra ancora LINQy, ma mi chiedo se c'è già qualcosa disponibile in System.Linq per quello.

MODIFICA: Avevo creato la mia estensione personale (come segue), ma Me.Name ha trovato qualcosa di molto intelligente nella sua risposta.

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 }; 
     } 
    } 
} 

risposta

10

Un vecchio trucco per trovare questo tipo di isole è sottrarre l'indice e il valore numerico. Il risultato rappresenterà gruppi unici. Utilizzando il sovraccarico di selezione tra cui l'indice:

(ToList usata qui, ma naturalmente ToArray può essere utilizzato se gli array sono preferiti)
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(); 

+0

Questo è un trucco carino per integers-- se il risultato (numero - indice) è 0 allora questo numero è nella posizione dell'array previsto, se il numero è negativo, allora è molto prima di dove è previsto. – Hogan

+0

Cosa succede se la mia sequenza non inizia con 1? supponiamo che questa sia la mia serie var x = new [] {20, 21, 22, 25,26,27, 30,31,32}; – fahadash

+1

@fahadash: funzionerà anche, l'intenzione è che la differenza tra l'indice e il valore sia maggiore se c'è un divario tra i valori. Per quella sequenza: 20-0 = 20, 21-1 = 20, 22 -2 = 20, 25 -3 = 22, 26-4 = 22, ecc. Quindi il valore 'group' salta da 20 a 22 –

1

ho po 'in ritardo, ma in ogni caso voglio condividere l'idea. Si potrebbe anche creare un iteratore come questo:

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; 
       } 
      } 
     } 
    } 
} 

Un'altra variante potrebbe essere utilizzando il metodo Aggregate estensione:

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; 
            }); 
Problemi correlati