2014-12-03 12 views
5

Ho un elenco ordinato di valori int e desidero creare valori vicini ai gruppi. I valori, dove next value è nextvalue> = prevValue + 1 sono vicini.Valori contigui del gruppo usando linq

Ad esempio: listino:

{1,2,3,5,6,8,9,10} 

Gruppi saranno:

{1,2,3} 
{5,6} 
{8,9,10} 

Questo potrebbe essere fatto utilizzando LINQ?

Questo può essere fatto facilmente senza linq - iterando l'elenco, ma mi chiedo se linq abbia una soluzione per questo.

+0

Linq è un requisito? – Magnus

+0

Sì, Linq è requisito. –

+0

Sei sicuro di avere la condizione corretta? Se 'nextvalue> = prevValue + 1' allora 3 e 5 sono vicini: prevVal = 3, nextVal = 5 -> prevVal + 1 = 4 -> 5> = 4 è vero quindi prevVal = 3 e nextVal = 5 sono vicini. Non dovrebbe essere: 'nextvalue == prevValue + 1'? – PiotrWolkowski

risposta

1

questo ha funzionato per me:

private static void GroupToNeighboursTest() 
{ 
    var numbers = new List<int> { 1, 2, 3, 5, 6, 8, 9, 10 }; 
    var neighbours = numbers 
     .Zip(numbers.Skip(1), Tuple.Create) 
     .Aggregate(new List<List<int>> { new List<int> { numbers.First() } }, (result, i) => 
     { 
      if (i.Item1 == i.Item2 - 1) 
      { 
       result.Last().Add(i.Item2); 
      } 
      else 
      { 
       result.Add(new List<int> { }); 
       result.Last().Add(i.Item2); 
      } 

      return result; 
     }); 
} 

enter image description here

+0

Ho appena testato questo codice e non produce l'output richiesto nella domanda. – Enigmativity

0

Ho solo una soluzione non linq per questo (se ho capito bene il problema). Spero che sia d'aiuto.

void Main() 
{ 
    var list = new List<int>{1,2,3,5,6,8,9,10}; 
    GetData(list).Dump(); 
} 

public IEnumerable<List<int>> GetData(IEnumerable<int> data) 
{ 
    int prev = 0; 
    var g = new List<int>(); 
    foreach (var item in data) 
    { 
     if(item - prev > 1 && g.Count > 0) 
     { 
      yield return g; 
      g = new List<int>(); 
     } 
     g.Add(item); 
     prev = item; 
    } 
    yield return g; 
} 
1

Se è necessario utilizzare LINQ, si potrebbe prendere in considerazione il metodo Aggregate.

x.Aggregate(new List<List<int>> { new List<int>() }, (soFar, next) => { 
    if (soFar.Last().Contains(next - 1)) { 
     soFar.Last().Add(next); 
    } else { 
     soFar.Add(new List<int> { next }); 
    } 
    return soFar; 
}); 
+2

'Contains' è un'operazione O (n). Anche questo sembra restituire un gruppo extra vuoto all'inizio. – Magnus

+0

Penso, dalla mia comprensione della domanda, che puoi cambiare la riga '.Contains (...)' a (ifFar.Last(). Last() == next - 1) {'. – Enigmativity

1

questo funziona per me:

var result = 
    items 
     .Skip(1) 
     .Aggregate(
      new [] { items.Take(1).ToList() }.ToList(), 
      (acc, item) => 
      { 
       if (acc.Last().Last() == item - 1) 
       { 
        acc.Last().Add(item); 
       } 
       else 
       { 
        acc.Add(new [] { item }.ToList()); 
       } 
       return acc; 
      }); 

ottengo questo risultato:

result

Come nota a margine, mi piace prendere l'abitudine di creare elenchi come questo new [] { item }.ToList() in quanto consente di compilare elenchi di tipi anonimi. Molto utile in molte query LINQ.